1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
31 #include <SidebarWindowsConsts.hxx>
32 #include "AnchorOverlayObject.hxx"
33 #include "ShadowOverlayObject.hxx"
36 #include <vcl/svapp.hxx>
37 #include <vcl/outdev.hxx>
38 #include <vcl/settings.hxx>
40 #include <chrdlgmodes.hxx>
41 #include <viewopt.hxx>
46 #include <IDocumentSettingAccess.hxx>
47 #include <IDocumentFieldsAccess.hxx>
50 #include <docufld.hxx>
53 #include <txtannotationfld.hxx>
54 #include <rootfrm.hxx>
55 #include <SwRewriter.hxx>
56 #include <tools/color.hxx>
57 #include <unotools/datetime.hxx>
59 #include <swmodule.hxx>
60 #include <strings.hrc>
63 #include <sfx2/request.hxx>
64 #include <sfx2/event.hxx>
65 #include <svl/srchitem.hxx>
67 #include <svl/languageoptions.hxx>
68 #include <svl/hint.hxx>
70 #include <svx/svdview.hxx>
71 #include <editeng/eeitem.hxx>
72 #include <editeng/langitem.hxx>
73 #include <editeng/outliner.hxx>
74 #include <editeng/outlobj.hxx>
76 #include <comphelper/lok.hxx>
77 #include <comphelper/string.hxx>
78 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
80 #include <annotsh.hxx>
81 #include <swabstdlg.hxx>
84 // distance between Anchor Y and initial note position
85 #define POSTIT_INITIAL_ANCHOR_DISTANCE 20
86 //distance between two postits
87 #define POSTIT_SPACE_BETWEEN 8
88 #define POSTIT_MINIMUMSIZE_WITH_META 60
89 #define POSTIT_SCROLL_SIDEBAR_HEIGHT 20
91 // if we layout more often we stop, this should never happen
92 #define MAX_LOOP_COUNT 50
94 using namespace sw::sidebarwindows
;
95 using namespace sw::annotation
;
99 enum class CommentNotificationType
{ Add
, Remove
, Modify
, Resolve
};
101 bool comp_pos(const std::unique_ptr
<SwSidebarItem
>& a
, const std::unique_ptr
<SwSidebarItem
>& b
)
103 // sort by anchor position
104 SwPosition aPosAnchorA
= a
->GetAnchorPosition();
105 SwPosition aPosAnchorB
= b
->GetAnchorPosition();
107 bool aAnchorAInFooter
= false;
108 bool aAnchorBInFooter
= false;
110 // is the anchor placed in Footnote or the Footer?
111 if( aPosAnchorA
.GetNode().FindFootnoteStartNode() || aPosAnchorA
.GetNode().FindFooterStartNode() )
112 aAnchorAInFooter
= true;
113 if( aPosAnchorB
.GetNode().FindFootnoteStartNode() || aPosAnchorB
.GetNode().FindFooterStartNode() )
114 aAnchorBInFooter
= true;
117 // if AnchorA is in footnote, and AnchorB isn't
118 // we do not want to change over the position
119 if( aAnchorAInFooter
&& !aAnchorBInFooter
)
121 // if aAnchorA is not placed in a footnote, and aAnchorB is
122 // force a change over
123 else if( !aAnchorAInFooter
&& aAnchorBInFooter
)
125 // If neither or both are in the footer, compare the positions.
126 // Since footnotes are in Inserts section of nodes array and footers
127 // in Autotext section, all footnotes precede any footers so no need
130 return aPosAnchorA
< aPosAnchorB
;
133 /// Emits LOK notification about one addition/removal/change of a comment
134 void lcl_CommentNotification(const SwView
* pView
, const CommentNotificationType nType
, const SwSidebarItem
* pItem
, const sal_uInt32 nPostItId
)
136 if (!comphelper::LibreOfficeKit::isActive())
139 boost::property_tree::ptree aAnnotation
;
140 aAnnotation
.put("action", (nType
== CommentNotificationType::Add
? "Add" :
141 (nType
== CommentNotificationType::Remove
? "Remove" :
142 (nType
== CommentNotificationType::Modify
? "Modify" :
143 (nType
== CommentNotificationType::Resolve
? "Resolve" : "???")))));
144 aAnnotation
.put("id", nPostItId
);
145 if (nType
!= CommentNotificationType::Remove
&& pItem
!= nullptr)
147 sw::annotation::SwAnnotationWin
* pWin
= pItem
->mpPostIt
.get();
149 const SwPostItField
* pField
= pWin
->GetPostItField();
150 const SwRect
& aRect
= pWin
->GetAnchorRect();
151 tools::Rectangle
aSVRect(aRect
.Pos().getX(),
153 aRect
.Pos().getX() + aRect
.SSize().Width(),
154 aRect
.Pos().getY() + aRect
.SSize().Height());
156 if (!pItem
->maLayoutInfo
.mPositionFromCommentAnchor
)
158 // Comments on frames: anchor position is the corner position, not the whole frame.
159 aSVRect
.SetSize(Size(0, 0));
162 std::vector
<OString
> aRects
;
163 for (const basegfx::B2DRange
& aRange
: pWin
->GetAnnotationTextRanges())
165 const SwRect
rect(aRange
.getMinX(), aRange
.getMinY(), aRange
.getWidth(), aRange
.getHeight());
166 aRects
.push_back(rect
.SVRect().toString());
168 const OString sRects
= comphelper::string::join("; ", aRects
);
170 aAnnotation
.put("id", pField
->GetPostItId());
171 aAnnotation
.put("parent", pWin
->CalcParent());
172 aAnnotation
.put("paraIdParent", pField
->GetParentId());
173 aAnnotation
.put("author", pField
->GetPar1().toUtf8().getStr());
174 aAnnotation
.put("text", pField
->GetPar2().toUtf8().getStr());
175 aAnnotation
.put("resolved", pField
->GetResolved() ? "true" : "false");
176 aAnnotation
.put("dateTime", utl::toISO8601(pField
->GetDateTime().GetUNODateTime()));
177 aAnnotation
.put("anchorPos", aSVRect
.toString());
178 aAnnotation
.put("textRange", sRects
.getStr());
181 boost::property_tree::ptree aTree
;
182 aTree
.add_child("comment", aAnnotation
);
183 std::stringstream aStream
;
184 boost::property_tree::write_json(aStream
, aTree
);
185 std::string aPayload
= aStream
.str();
189 pView
->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT
, OString(aPayload
));
193 } // anonymous namespace
195 SwPostItMgr::SwPostItMgr(SwView
* pView
)
197 , mpWrtShell(mpView
->GetDocShell()->GetWrtShell())
198 , mpEditWin(&mpView
->GetEditWin())
200 , mbWaitingForCalcRects(false)
201 , mpActivePostIt(nullptr)
205 , mbReadOnly(mpView
->GetDocShell()->IsReadOnly())
207 , mbIsShowAnchor( false )
209 if(!mpView
->GetDrawView() )
210 mpView
->GetWrtShell().MakeDrawView();
213 mbIsShowAnchor
= aProps
.IsShowAnchor();
215 //make sure we get the colour yellow always, even if not the first one of comments or redlining
216 SW_MOD()->GetRedlineAuthor();
218 // collect all PostIts and redline comments that exist after loading the document
219 // don't check for existence for any of them, don't focus them
220 AddPostIts(false,false);
221 /* this code can be used once we want redline comments in the Sidebar
222 AddRedlineComments(false,false);
224 // we want to receive stuff like SfxHintId::DocChanged
225 StartListening(*mpView
->GetDocShell());
226 if (!mvPostItFields
.empty())
228 mbWaitingForCalcRects
= true;
229 mnEventId
= Application::PostUserEvent( LINK( this, SwPostItMgr
, CalcHdl
) );
233 SwPostItMgr::~SwPostItMgr()
236 Application::RemoveUserEvent( mnEventId
);
237 // forget about all our Sidebar windows
239 EndListening( *mpView
->GetDocShell() );
244 bool SwPostItMgr::CheckForRemovedPostIts()
246 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
247 bool bRemoved
= false;
248 auto it
= mvPostItFields
.begin();
249 while(it
!= mvPostItFields
.end())
251 if (!(*it
)->UseElement(*mpWrtShell
->GetLayout(), rIDRA
))
253 EndListening(const_cast<SfxBroadcaster
&>(*(*it
)->GetBroadcaster()));
255 if((*it
)->mpPostIt
&& (*it
)->mpPostIt
->GetPostItField())
256 lcl_CommentNotification(mpView
, CommentNotificationType::Remove
, nullptr, (*it
)->mpPostIt
->GetPostItField()->GetPostItId());
258 std::unique_ptr
<SwSidebarItem
> p
= std::move(*it
);
259 it
= mvPostItFields
.erase(it
);
260 if (GetActiveSidebarWin() == p
->mpPostIt
)
261 SetActiveSidebarWin(nullptr);
262 p
->mpPostIt
.disposeAndClear();
264 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
266 const SwPostItField
* pPostItField
= static_cast<const SwPostItField
*>(p
->GetFormatField().GetField());
267 lcl_CommentNotification(mpView
, CommentNotificationType::Remove
, nullptr, pPostItField
->GetPostItId());
279 // make sure that no deleted items remain in page lists
280 // todo: only remove deleted ones?!
281 if ( mvPostItFields
.empty() )
283 PreparePageContainer();
288 // if postits are there make sure that page lists are not empty
289 // otherwise sudden paints can cause pain (in BorderOverPageBorder)
296 SwSidebarItem
* SwPostItMgr::InsertItem(SfxBroadcaster
* pItem
, bool bCheckExistence
, bool bFocus
)
300 for (auto const& postItField
: mvPostItFields
)
302 if ( postItField
->GetBroadcaster() == pItem
)
308 SwSidebarItem
* pAnnotationItem
= nullptr;
309 if (auto pSwFormatField
= dynamic_cast< SwFormatField
*>( pItem
))
311 mvPostItFields
.push_back(std::make_unique
<SwAnnotationItem
>(*pSwFormatField
, bFocus
));
312 pAnnotationItem
= mvPostItFields
.back().get();
314 assert(dynamic_cast< const SwFormatField
*>( pItem
) && "Mgr::InsertItem: seems like new stuff was added");
315 StartListening(*pItem
);
316 return pAnnotationItem
;
319 void SwPostItMgr::RemoveItem( SfxBroadcaster
* pBroadcast
)
321 EndListening(*pBroadcast
);
322 auto i
= std::find_if(mvPostItFields
.begin(), mvPostItFields
.end(),
323 [&pBroadcast
](const std::unique_ptr
<SwSidebarItem
>& pField
) { return pField
->GetBroadcaster() == pBroadcast
; });
324 if (i
!= mvPostItFields
.end())
326 std::unique_ptr
<SwSidebarItem
> p
= std::move(*i
);
327 // tdf#120487 remove from list before dispose, so comment window
328 // won't be recreated due to the entry still in the list if focus
329 // transferring from the pPostIt triggers relayout of postits
330 // tdf#133348 remove from list before calling SetActiveSidebarWin
331 // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr
332 mvPostItFields
.erase(i
);
333 if (GetActiveSidebarWin() == p
->mpPostIt
)
334 SetActiveSidebarWin(nullptr);
335 p
->mpPostIt
.disposeAndClear();
341 void SwPostItMgr::Notify( SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
343 if ( const SfxEventHint
* pSfxEventHint
= dynamic_cast<const SfxEventHint
*>(&rHint
) )
345 if ( pSfxEventHint
->GetEventId() == SfxEventHintId::SwEventLayoutFinished
)
347 if ( !mbWaitingForCalcRects
&& !mvPostItFields
.empty())
349 mbWaitingForCalcRects
= true;
350 mnEventId
= Application::PostUserEvent( LINK( this, SwPostItMgr
, CalcHdl
) );
354 else if ( const SwFormatFieldHint
* pFormatHint
= dynamic_cast<const SwFormatFieldHint
*>(&rHint
) )
356 SwFormatField
* pField
= const_cast <SwFormatField
*>( pFormatHint
->GetField() );
357 switch ( pFormatHint
->Which() )
359 case SwFormatFieldHintWhich::INSERTED
:
366 // get field to be inserted from hint
367 if ( pField
->IsFieldInDoc() )
369 bool bEmpty
= !HasNotes();
370 SwSidebarItem
* pItem
= InsertItem( pField
, true, false );
372 if (bEmpty
&& !mvPostItFields
.empty())
375 // True until the layout of this post it finishes
377 pItem
->mbPendingLayout
= true;
381 OSL_FAIL("Inserted field not in document!" );
385 case SwFormatFieldHintWhich::REMOVED
:
391 const bool bWasRemoved
= CheckForRemovedPostIts();
392 // tdf#143643 ensure relayout on undo of insert comment
399 // If LOK has disabled tiled annotations, emit annotation callbacks
400 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
402 SwPostItField
* pPostItField
= static_cast<SwPostItField
*>(pField
->GetField());
403 lcl_CommentNotification(mpView
, CommentNotificationType::Remove
, nullptr, pPostItField
->GetPostItId());
408 case SwFormatFieldHintWhich::FOCUS
:
410 if (pFormatHint
->GetView()== mpView
)
414 case SwFormatFieldHintWhich::CHANGED
:
415 case SwFormatFieldHintWhich::RESOLVED
:
417 SwFormatField
* pFormatField
= dynamic_cast<SwFormatField
*>(&rBC
);
418 for (auto const& postItField
: mvPostItFields
)
420 if ( pFormatField
== postItField
->GetBroadcaster() )
422 if (postItField
->mpPostIt
)
424 postItField
->mpPostIt
->SetPostItText();
428 // If LOK has disabled tiled annotations, emit annotation callbacks
429 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
431 if(SwFormatFieldHintWhich::CHANGED
== pFormatHint
->Which())
432 lcl_CommentNotification(mpView
, CommentNotificationType::Modify
, postItField
.get(), 0);
434 lcl_CommentNotification(mpView
, CommentNotificationType::Resolve
, postItField
.get(), 0);
442 case SwFormatFieldHintWhich::LANGUAGE
:
444 SwFormatField
* pFormatField
= dynamic_cast<SwFormatField
*>(&rBC
);
445 for (auto const& postItField
: mvPostItFields
)
447 if ( pFormatField
== postItField
->GetBroadcaster() )
449 if (postItField
->mpPostIt
)
451 const SvtScriptType nScriptType
= SvtLanguageOptions::GetScriptTypeOfLanguage( postItField
->GetFormatField().GetField()->GetLanguage() );
452 sal_uInt16 nLangWhichId
= 0;
455 case SvtScriptType::LATIN
: nLangWhichId
= EE_CHAR_LANGUAGE
; break;
456 case SvtScriptType::ASIAN
: nLangWhichId
= EE_CHAR_LANGUAGE_CJK
; break;
457 case SvtScriptType::COMPLEX
: nLangWhichId
= EE_CHAR_LANGUAGE_CTL
; break;
460 postItField
->mpPostIt
->SetLanguage(
462 postItField
->GetFormatField().GetField()->GetLanguage(),
474 SfxHintId nId
= rHint
.GetId();
477 case SfxHintId::ModeChanged
:
479 if ( mbReadOnly
!= mpView
->GetDocShell()->IsReadOnly() )
481 mbReadOnly
= !mbReadOnly
;
487 case SfxHintId::DocChanged
:
489 if ( mpView
->GetDocShell() == &rBC
)
491 if ( !mbWaitingForCalcRects
&& !mvPostItFields
.empty())
493 mbWaitingForCalcRects
= true;
494 mnEventId
= Application::PostUserEvent( LINK( this, SwPostItMgr
, CalcHdl
) );
499 case SfxHintId::SwSplitNodeOperation
:
501 // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker
502 mbDeleteNote
= !mbDeleteNote
;
505 case SfxHintId::Dying
:
507 if ( mpView
->GetDocShell() != &rBC
)
509 // field to be removed is the broadcaster
510 OSL_FAIL("Notification for removed SwFormatField was not sent!");
520 void SwPostItMgr::Focus(const SfxBroadcaster
& rBC
)
522 if (!mpWrtShell
->GetViewOptions()->IsPostIts())
524 SfxRequest
aRequest(mpView
->GetViewFrame(), SID_TOGGLE_NOTES
);
525 mpView
->ExecViewOptions(aRequest
);
528 for (auto const& postItField
: mvPostItFields
)
530 // field to get the focus is the broadcaster
531 if ( &rBC
== postItField
->GetBroadcaster() )
533 if (postItField
->mpPostIt
)
535 if (postItField
->mpPostIt
->IsResolved() &&
536 !mpWrtShell
->GetViewOptions()->IsResolvedPostIts())
538 SfxRequest
aRequest(mpView
->GetViewFrame(), SID_TOGGLE_RESOLVED_NOTES
);
539 mpView
->ExecViewOptions(aRequest
);
541 postItField
->mpPostIt
->GrabFocus();
542 MakeVisible(postItField
->mpPostIt
);
546 // when the layout algorithm starts, this postit is created and receives focus
547 postItField
->mbFocus
= true;
553 bool SwPostItMgr::CalcRects()
557 // if CalcRects() was forced and an event is still pending: remove it
558 // it is superfluous and also may cause reentrance problems if triggered while layouting
559 Application::RemoveUserEvent( mnEventId
);
563 bool bChange
= false;
564 bool bRepair
= false;
565 PreparePageContainer();
566 if ( !mvPostItFields
.empty() )
568 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
569 for (auto const& pItem
: mvPostItFields
)
571 if (!pItem
->UseElement(*mpWrtShell
->GetLayout(), rIDRA
))
573 OSL_FAIL("PostIt is not in doc or other wrong use");
577 const SwRect
aOldAnchorRect( pItem
->maLayoutInfo
.mPosition
);
578 const SwPostItHelper::SwLayoutStatus eOldLayoutStatus
= pItem
->mLayoutStatus
;
579 const SwNodeOffset
nOldStartNodeIdx( pItem
->maLayoutInfo
.mnStartNodeIdx
);
580 const sal_Int32
nOldStartContent( pItem
->maLayoutInfo
.mnStartContent
);
582 // update layout information
583 const SwTextAnnotationField
* pTextAnnotationField
=
584 dynamic_cast< const SwTextAnnotationField
* >( pItem
->GetFormatField().GetTextField() );
585 const ::sw::mark::IMark
* pAnnotationMark
=
586 pTextAnnotationField
!= nullptr ? pTextAnnotationField
->GetAnnotationMark() : nullptr;
587 if ( pAnnotationMark
!= nullptr )
589 pItem
->mLayoutStatus
=
590 SwPostItHelper::getLayoutInfos(
592 pItem
->GetAnchorPosition(),
597 pItem
->mLayoutStatus
=
598 SwPostItHelper::getLayoutInfos( pItem
->maLayoutInfo
, pItem
->GetAnchorPosition() );
602 || pItem
->maLayoutInfo
.mPosition
!= aOldAnchorRect
603 || pItem
->mLayoutStatus
!= eOldLayoutStatus
604 || pItem
->maLayoutInfo
.mnStartNodeIdx
!= nOldStartNodeIdx
605 || pItem
->maLayoutInfo
.mnStartContent
!= nOldStartContent
;
608 // show notes in right order in navigator
609 //prevent Anchors during layout to overlap, e.g. when moving a frame
610 if (mvPostItFields
.size()>1 )
611 std::stable_sort(mvPostItFields
.begin(), mvPostItFields
.end(), comp_pos
);
613 // sort the items into the right page vector, so layout can be done by page
614 for (auto const& pItem
: mvPostItFields
)
616 if( SwPostItHelper::INVISIBLE
== pItem
->mLayoutStatus
)
619 pItem
->mpPostIt
->HideNote();
623 if( SwPostItHelper::HIDDEN
== pItem
->mLayoutStatus
)
625 if (!mpWrtShell
->GetViewOptions()->IsShowHiddenChar())
628 pItem
->mpPostIt
->HideNote();
633 const tools::ULong aPageNum
= pItem
->maLayoutInfo
.mnPageNumber
;
634 if (aPageNum
> mPages
.size())
636 const tools::ULong nNumberOfPages
= mPages
.size();
637 mPages
.reserve(aPageNum
);
638 for (tools::ULong j
=0; j
<aPageNum
- nNumberOfPages
; ++j
)
639 mPages
.emplace_back( new SwPostItPageItem());
641 mPages
[aPageNum
-1]->mvSidebarItems
.push_back(pItem
.get());
642 mPages
[aPageNum
-1]->mPageRect
= pItem
->maLayoutInfo
.mPageFrame
;
643 mPages
[aPageNum
-1]->eSidebarPosition
= pItem
->maLayoutInfo
.meSidebarPosition
;
646 if (!bChange
&& mpWrtShell
->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE
))
648 tools::Long nLayoutHeight
= SwPostItHelper::getLayoutHeight( mpWrtShell
->GetLayout() );
649 if( nLayoutHeight
> mbLayoutHeight
)
651 if (mPages
[0]->bScrollbar
|| HasScrollbars())
654 else if( nLayoutHeight
< mbLayoutHeight
)
656 if (mPages
[0]->bScrollbar
|| !BorderOverPageBorder(1))
663 CheckForRemovedPostIts();
665 mbLayoutHeight
= SwPostItHelper::getLayoutHeight( mpWrtShell
->GetLayout() );
666 mbWaitingForCalcRects
= false;
670 bool SwPostItMgr::HasScrollbars() const
672 for (auto const& postItField
: mvPostItFields
)
674 if (postItField
->mbShow
&& postItField
->mpPostIt
&& postItField
->mpPostIt
->HasScrollbar())
680 void SwPostItMgr::PreparePageContainer()
682 // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost
683 tools::Long lPageSize
= mpWrtShell
->GetNumPages();
684 tools::Long lContainerSize
= mPages
.size();
686 if (lContainerSize
< lPageSize
)
688 mPages
.reserve(lPageSize
);
689 for (tools::Long i
=0; i
<lPageSize
- lContainerSize
;i
++)
690 mPages
.emplace_back( new SwPostItPageItem());
692 else if (lContainerSize
> lPageSize
)
694 for (int i
=mPages
.size()-1; i
>= lPageSize
;--i
)
699 // only clear the list, DO NOT delete the objects itself
700 for (auto const& page
: mPages
)
702 page
->mvSidebarItems
.clear();
703 if (mvPostItFields
.empty())
704 page
->bScrollbar
= false;
708 void SwPostItMgr::LayoutPostIts()
710 bool bEnableMapMode
= comphelper::LibreOfficeKit::isActive() && !mpEditWin
->IsMapModeEnabled();
712 mpEditWin
->EnableMapMode();
714 if ( !mvPostItFields
.empty() && !mbWaitingForCalcRects
)
718 //loop over all pages and do the layout
719 // - create SwPostIt if necessary
720 // - place SwPostIts on their initial position
721 // - calculate necessary height for all PostIts together
722 bool bUpdate
= false;
723 for (std::unique_ptr
<SwPostItPageItem
>& pPage
: mPages
)
725 // only layout if there are notes on this page
726 if (!pPage
->mvSidebarItems
.empty())
728 std::vector
<SwAnnotationWin
*> aVisiblePostItList
;
729 tools::ULong lNeededHeight
= 0;
730 tools::Long mlPageBorder
= 0;
731 tools::Long mlPageEnd
= 0;
733 for (auto const& pItem
: pPage
->mvSidebarItems
)
735 VclPtr
<SwAnnotationWin
> pPostIt
= pItem
->mpPostIt
;
737 if (pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
)
739 // x value for notes positioning
740 mlPageBorder
= mpEditWin
->LogicToPixel( Point( pPage
->mPageRect
.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true);
743 mpWrtShell
->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE
)
744 ? pItem
->maLayoutInfo
.mPagePrtArea
.Left()
745 : pPage
->mPageRect
.Left() + 350;
747 else if (pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::RIGHT
)
749 // x value for notes positioning
750 mlPageBorder
= mpEditWin
->LogicToPixel( Point(pPage
->mPageRect
.Right(), 0)).X() + GetSidebarBorderWidth(true);
753 mpWrtShell
->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE
)
754 ? pItem
->maLayoutInfo
.mPagePrtArea
.Right() :
755 pPage
->mPageRect
.Right() - 350;
760 tools::Long Y
= mpEditWin
->LogicToPixel( Point(0,pItem
->maLayoutInfo
.mPosition
.Bottom())).Y();
761 tools::Long aPostItHeight
= 0;
764 pPostIt
= pItem
->GetSidebarWindow( mpView
->GetEditWin(),
766 pPostIt
->InitControls();
767 pPostIt
->SetReadonly(mbReadOnly
);
768 pItem
->mpPostIt
= pPostIt
;
771 if (static_cast<bool>(pPostIt
->CalcParent())) //do we really have another note in front of this one
772 pPostIt
->InitAnswer(*mpAnswer
);
777 pPostIt
->SetChangeTracking(
778 pItem
->mLayoutStatus
,
779 GetColorAnchor(pItem
->maLayoutInfo
.mRedlineAuthor
));
780 pPostIt
->SetSidebarPosition(pPage
->eSidebarPosition
);
781 pPostIt
->SetFollow(static_cast<bool>(pPostIt
->CalcParent()));
782 aPostItHeight
= ( pPostIt
->GetPostItTextHeight() < pPostIt
->GetMinimumSizeWithoutMeta()
783 ? pPostIt
->GetMinimumSizeWithoutMeta()
784 : pPostIt
->GetPostItTextHeight() )
785 + pPostIt
->GetMetaHeight();
786 pPostIt
->SetPosSizePixelRect( mlPageBorder
,
787 Y
- GetInitialAnchorDistance(),
788 GetSidebarWidth(true),
790 pItem
->maLayoutInfo
.mPosition
,
792 pPostIt
->ChangeSidebarItem( *pItem
);
797 pPostIt
->GrabFocus();
798 pItem
->mbFocus
= false;
800 // only the visible postits are used for the final layout
801 aVisiblePostItList
.push_back(pPostIt
);
802 lNeededHeight
+= pPostIt
->IsFollow() ? aPostItHeight
: aPostItHeight
+GetSpaceBetween();
804 else // we don't want to see it
811 if ((!aVisiblePostItList
.empty()) && ShowNotes())
813 bool bOldScrollbar
= pPage
->bScrollbar
;
815 pPage
->bScrollbar
= LayoutByPage(aVisiblePostItList
, pPage
->mPageRect
.SVRect(), lNeededHeight
);
817 pPage
->bScrollbar
= false;
818 if (!pPage
->bScrollbar
)
822 else if (sal_Int32 nScrollSize
= GetScrollSize())
824 //when we changed our zoom level, the offset value can be too big, so lets check for the largest possible zoom value
825 tools::Long aAvailableHeight
= mpEditWin
->LogicToPixel(Size(0,pPage
->mPageRect
.Height())).Height() - 2 * GetSidebarScrollerHeight();
826 tools::Long lOffset
= -1 * nScrollSize
* (aVisiblePostItList
.size() - aAvailableHeight
/ nScrollSize
);
827 if (pPage
->lOffset
< lOffset
)
828 pPage
->lOffset
= lOffset
;
830 bUpdate
= (bOldScrollbar
!= pPage
->bScrollbar
) || bUpdate
;
831 const tools::Long aSidebarheight
= pPage
->bScrollbar
? mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
834 - enlarge all notes till GetNextBorder(), as we resized to average value before
836 //lets hide the ones which overlap the page
837 for (auto const& visiblePostIt
: aVisiblePostItList
)
839 if (pPage
->lOffset
!= 0)
840 visiblePostIt
->TranslateTopPosition(pPage
->lOffset
);
842 bool bBottom
= mpEditWin
->PixelToLogic(Point(0,visiblePostIt
->VirtualPos().Y()+visiblePostIt
->VirtualSize().Height())).Y() <= (pPage
->mPageRect
.Bottom()-aSidebarheight
);
843 bool bTop
= mpEditWin
->PixelToLogic(Point(0,visiblePostIt
->VirtualPos().Y())).Y() >= (pPage
->mPageRect
.Top()+aSidebarheight
);
844 if ( bBottom
&& bTop
)
846 // When tiled rendering, make sure that only the
847 // view that has the comment focus emits callbacks,
848 // so the editing view jumps to the comment, but
850 bool bTiledPainting
= comphelper::LibreOfficeKit::isTiledPainting();
852 // No focus -> disable callbacks.
853 comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt
->HasChildPathFocus());
854 visiblePostIt
->ShowNote();
856 comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting
);
860 if (mpEditWin
->PixelToLogic(Point(0,visiblePostIt
->VirtualPos().Y())).Y() < (pPage
->mPageRect
.Top()+aSidebarheight
))
862 if ( pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
)
863 visiblePostIt
->ShowAnchorOnly(Point( pPage
->mPageRect
.Left(),
864 pPage
->mPageRect
.Top()));
865 else if ( pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::RIGHT
)
866 visiblePostIt
->ShowAnchorOnly(Point( pPage
->mPageRect
.Right(),
867 pPage
->mPageRect
.Top()));
871 if ( pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
)
872 visiblePostIt
->ShowAnchorOnly(Point(pPage
->mPageRect
.Left(),
873 pPage
->mPageRect
.Bottom()));
874 else if ( pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::RIGHT
)
875 visiblePostIt
->ShowAnchorOnly(Point(pPage
->mPageRect
.Right(),
876 pPage
->mPageRect
.Bottom()));
878 OSL_ENSURE(pPage
->bScrollbar
,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true");
884 for (auto const& visiblePostIt
: aVisiblePostItList
)
886 visiblePostIt
->SetPosAndSize();
889 bool bOldScrollbar
= pPage
->bScrollbar
;
890 pPage
->bScrollbar
= false;
891 bUpdate
= (bOldScrollbar
!= pPage
->bScrollbar
) || bUpdate
;
894 for (auto const& visiblePostIt
: aVisiblePostItList
)
896 if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
898 if (visiblePostIt
->GetSidebarItem().mbPendingLayout
)
899 lcl_CommentNotification(mpView
, CommentNotificationType::Add
, &visiblePostIt
->GetSidebarItem(), 0);
900 else if (visiblePostIt
->IsAnchorRectChanged())
902 lcl_CommentNotification(mpView
, CommentNotificationType::Modify
, &visiblePostIt
->GetSidebarItem(), 0);
903 visiblePostIt
->ResetAnchorRectChanged();
907 // Layout for this post it finished now
908 visiblePostIt
->GetSidebarItem().mbPendingLayout
= false;
913 if (pPage
->bScrollbar
)
915 pPage
->bScrollbar
= false;
920 { // we do not want to see the notes anymore -> Options-Writer-View-Notes
921 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
922 bool bRepair
= false;
923 for (auto const& postItField
: mvPostItFields
)
925 if (!postItField
->UseElement(*mpWrtShell
->GetLayout(), rIDRA
))
927 OSL_FAIL("PostIt is not in doc!");
932 if (postItField
->mpPostIt
)
934 postItField
->mpPostIt
->HideNote();
935 if (postItField
->mpPostIt
->HasChildPathFocus())
937 SetActiveSidebarWin(nullptr);
938 postItField
->mpPostIt
->GrabFocusToDocument();
944 CheckForRemovedPostIts();
947 // notes scrollbar is otherwise not drawn correctly for some cases
948 // scrollbar area is enough
950 mpEditWin
->Invalidate(); /*This is a super expensive relayout and render of the entire page*/
956 mpEditWin
->EnableMapMode(false);
959 bool SwPostItMgr::BorderOverPageBorder(tools::ULong aPage
) const
961 if ( mPages
[aPage
-1]->mvSidebarItems
.empty() )
963 OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!");
967 auto aItem
= mPages
[aPage
-1]->mvSidebarItems
.end();
969 OSL_ENSURE ((*aItem
)->mpPostIt
,"BorderOverPageBorder: NULL postIt, should never happen");
970 if ((*aItem
)->mpPostIt
)
972 const tools::Long aSidebarheight
= mPages
[aPage
-1]->bScrollbar
? mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
973 const tools::Long aEndValue
= mpEditWin
->PixelToLogic(Point(0,(*aItem
)->mpPostIt
->GetPosPixel().Y()+(*aItem
)->mpPostIt
->GetSizePixel().Height())).Y();
974 return aEndValue
<= mPages
[aPage
-1]->mPageRect
.Bottom()-aSidebarheight
;
980 void SwPostItMgr::DrawNotesForPage(OutputDevice
*pOutDev
, sal_uInt32 nPage
)
982 assert(nPage
< mPages
.size());
983 if (nPage
>= mPages
.size())
985 for (auto const& pItem
: mPages
[nPage
]->mvSidebarItems
)
987 SwAnnotationWin
* pPostIt
= pItem
->mpPostIt
;
990 Point
aPoint(mpEditWin
->PixelToLogic(pPostIt
->GetPosPixel()));
991 pPostIt
->DrawForPage(pOutDev
, aPoint
);
995 void SwPostItMgr::PaintTile(OutputDevice
& rRenderContext
)
997 for (const std::unique_ptr
<SwSidebarItem
>& pItem
: mvPostItFields
)
999 SwAnnotationWin
* pPostIt
= pItem
->mpPostIt
;
1003 bool bEnableMapMode
= !mpEditWin
->IsMapModeEnabled();
1004 mpEditWin
->EnableMapMode();
1005 rRenderContext
.Push(vcl::PushFlags::MAPMODE
);
1006 Point
aOffset(mpEditWin
->PixelToLogic(pPostIt
->GetPosPixel()));
1007 MapMode
aMapMode(rRenderContext
.GetMapMode());
1008 aMapMode
.SetOrigin(aMapMode
.GetOrigin() + aOffset
);
1009 rRenderContext
.SetMapMode(aMapMode
);
1010 Size
aSize(rRenderContext
.PixelToLogic(pPostIt
->GetSizePixel()));
1011 tools::Rectangle
aRectangle(Point(0, 0), aSize
);
1013 pPostIt
->PaintTile(rRenderContext
, aRectangle
);
1015 rRenderContext
.Pop();
1017 mpEditWin
->EnableMapMode(false);
1021 void SwPostItMgr::Scroll(const tools::Long lScroll
,const tools::ULong aPage
)
1023 OSL_ENSURE((lScroll
% GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value");
1024 // do not scroll more than necessary up or down
1025 if ( ((mPages
[aPage
-1]->lOffset
== 0) && (lScroll
>0)) || ( BorderOverPageBorder(aPage
) && (lScroll
<0)) )
1028 const bool bOldUp
= ArrowEnabled(KEY_PAGEUP
,aPage
);
1029 const bool bOldDown
= ArrowEnabled(KEY_PAGEDOWN
,aPage
);
1030 const tools::Long aSidebarheight
= mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1031 for (auto const& item
: mPages
[aPage
-1]->mvSidebarItems
)
1033 SwAnnotationWin
* pPostIt
= item
->mpPostIt
;
1034 // if this is an answer, we should take the normal position and not the real, slightly moved position
1035 pPostIt
->SetVirtualPosSize(pPostIt
->GetPosPixel(),pPostIt
->GetSizePixel());
1036 pPostIt
->TranslateTopPosition(lScroll
);
1040 bool bBottom
= mpEditWin
->PixelToLogic(Point(0,pPostIt
->VirtualPos().Y()+pPostIt
->VirtualSize().Height())).Y() <= (mPages
[aPage
-1]->mPageRect
.Bottom()-aSidebarheight
);
1041 bool bTop
= mpEditWin
->PixelToLogic(Point(0,pPostIt
->VirtualPos().Y())).Y() >= (mPages
[aPage
-1]->mPageRect
.Top()+aSidebarheight
);
1042 if ( bBottom
&& bTop
)
1044 pPostIt
->ShowNote();
1048 if ( mpEditWin
->PixelToLogic(Point(0,pPostIt
->VirtualPos().Y())).Y() < (mPages
[aPage
-1]->mPageRect
.Top()+aSidebarheight
))
1050 if (mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
)
1051 pPostIt
->ShowAnchorOnly(Point(mPages
[aPage
-1]->mPageRect
.Left(),mPages
[aPage
-1]->mPageRect
.Top()));
1052 else if (mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::RIGHT
)
1053 pPostIt
->ShowAnchorOnly(Point(mPages
[aPage
-1]->mPageRect
.Right(),mPages
[aPage
-1]->mPageRect
.Top()));
1057 if (mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
)
1058 pPostIt
->ShowAnchorOnly(Point(mPages
[aPage
-1]->mPageRect
.Left(),mPages
[aPage
-1]->mPageRect
.Bottom()));
1059 else if (mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::RIGHT
)
1060 pPostIt
->ShowAnchorOnly(Point(mPages
[aPage
-1]->mPageRect
.Right(),mPages
[aPage
-1]->mPageRect
.Bottom()));
1065 mPages
[aPage
-1]->lOffset
+= lScroll
;
1066 if ( (bOldUp
!= ArrowEnabled(KEY_PAGEUP
,aPage
)) ||(bOldDown
!= ArrowEnabled(KEY_PAGEDOWN
,aPage
)) )
1068 mpEditWin
->Invalidate(GetBottomScrollRect(aPage
));
1069 mpEditWin
->Invalidate(GetTopScrollRect(aPage
));
1073 void SwPostItMgr::AutoScroll(const SwAnnotationWin
* pPostIt
,const tools::ULong aPage
)
1075 // otherwise all notes are visible
1076 if (!mPages
[aPage
-1]->bScrollbar
)
1079 const tools::Long aSidebarheight
= mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1080 const bool bBottom
= mpEditWin
->PixelToLogic(Point(0,pPostIt
->GetPosPixel().Y()+pPostIt
->GetSizePixel().Height())).Y() <= (mPages
[aPage
-1]->mPageRect
.Bottom()-aSidebarheight
);
1081 const bool bTop
= mpEditWin
->PixelToLogic(Point(0,pPostIt
->GetPosPixel().Y())).Y() >= (mPages
[aPage
-1]->mPageRect
.Top()+aSidebarheight
);
1082 if ( !(bBottom
&& bTop
))
1084 const tools::Long aDiff
= bBottom
? mpEditWin
->LogicToPixel(Point(0,mPages
[aPage
-1]->mPageRect
.Top() + aSidebarheight
)).Y() - pPostIt
->GetPosPixel().Y() :
1085 mpEditWin
->LogicToPixel(Point(0,mPages
[aPage
-1]->mPageRect
.Bottom() - aSidebarheight
)).Y() - (pPostIt
->GetPosPixel().Y()+pPostIt
->GetSizePixel().Height());
1086 // this just adds the missing value to get the next a* GetScrollSize() after aDiff
1087 // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100
1088 const auto nScrollSize
= GetScrollSize();
1089 assert(nScrollSize
);
1090 const tools::Long lScroll
= bBottom
? (aDiff
+ ( nScrollSize
- (aDiff
% nScrollSize
))) : (aDiff
- (nScrollSize
+ (aDiff
% nScrollSize
)));
1091 Scroll(lScroll
, aPage
);
1095 void SwPostItMgr::MakeVisible(const SwAnnotationWin
* pPostIt
)
1097 tools::Long aPage
= -1;
1098 // we don't know the page yet, lets find it ourselves
1099 std::vector
<SwPostItPageItem
*>::size_type n
=0;
1100 for (auto const& page
: mPages
)
1102 for (auto const& item
: page
->mvSidebarItems
)
1104 if (item
->mpPostIt
==pPostIt
)
1113 AutoScroll(pPostIt
,aPage
);
1114 tools::Rectangle
aNoteRect (Point(pPostIt
->GetPosPixel().X(),pPostIt
->GetPosPixel().Y()-5),pPostIt
->GetSizePixel());
1115 if (!aNoteRect
.IsEmpty())
1116 mpWrtShell
->MakeVisible(SwRect(mpEditWin
->PixelToLogic(aNoteRect
)));
1119 bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection
,tools::ULong aPage
) const
1125 return (mPages
[aPage
-1]->lOffset
!= 0);
1129 return (!BorderOverPageBorder(aPage
));
1131 default: return false;
1135 Color
SwPostItMgr::GetArrowColor(sal_uInt16 aDirection
,tools::ULong aPage
) const
1137 if (ArrowEnabled(aDirection
,aPage
))
1139 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1142 return COL_NOTES_SIDEPANE_ARROW_ENABLED
;
1146 return COL_NOTES_SIDEPANE_ARROW_DISABLED
;
1150 bool SwPostItMgr::LayoutByPage(std::vector
<SwAnnotationWin
*> &aVisiblePostItList
, const tools::Rectangle
& rBorder
, tools::Long lNeededHeight
)
1152 /*** General layout idea:***/
1153 // - if we have space left, we always move the current one up,
1154 // otherwise the next one down
1155 // - first all notes are resized
1156 // - then the real layout starts
1158 //rBorder is the page rect
1159 const tools::Rectangle aBorder
= mpEditWin
->LogicToPixel(rBorder
);
1160 tools::Long lTopBorder
= aBorder
.Top() + 5;
1161 tools::Long lBottomBorder
= aBorder
.Bottom() - 5;
1162 const tools::Long lVisibleHeight
= lBottomBorder
- lTopBorder
; //aBorder.GetHeight() ;
1163 const size_t nPostItListSize
= aVisiblePostItList
.size();
1164 tools::Long lTranslatePos
= 0;
1165 bool bScrollbars
= false;
1167 // do all necessary resizings
1168 if (nPostItListSize
> 0 && lVisibleHeight
< lNeededHeight
)
1170 // ok, now we have to really resize and adding scrollbars
1171 const tools::Long lAverageHeight
= (lVisibleHeight
- nPostItListSize
*GetSpaceBetween()) / nPostItListSize
;
1172 if (lAverageHeight
<GetMinimumSizeWithMeta())
1175 lTopBorder
+= GetSidebarScrollerHeight() + 10;
1176 lBottomBorder
-= (GetSidebarScrollerHeight() + 10);
1177 for (auto const& visiblePostIt
: aVisiblePostItList
)
1178 visiblePostIt
->SetSize(Size(visiblePostIt
->VirtualSize().getWidth(),visiblePostIt
->GetMinimumSizeWithMeta()));
1182 for (auto const& visiblePostIt
: aVisiblePostItList
)
1184 if ( visiblePostIt
->VirtualSize().getHeight() > lAverageHeight
)
1185 visiblePostIt
->SetSize(Size(visiblePostIt
->VirtualSize().getWidth(),lAverageHeight
));
1190 //start the real layout so nothing overlaps anymore
1191 if (aVisiblePostItList
.size()>1)
1195 // if no window is moved anymore we are finished
1200 tools::Long lSpaceUsed
= lTopBorder
+ GetSpaceBetween();
1201 for(auto i
= aVisiblePostItList
.begin(); i
!= aVisiblePostItList
.end() ; ++i
)
1203 auto aNextPostIt
= i
;
1206 if (aNextPostIt
!= aVisiblePostItList
.end())
1208 lTranslatePos
= ( (*i
)->VirtualPos().Y() + (*i
)->VirtualSize().Height()) - (*aNextPostIt
)->VirtualPos().Y();
1209 if (lTranslatePos
> 0) // note windows overlaps the next one
1211 // we are not done yet, loop at least once more
1213 // if there is space left, move the current note up
1214 // it could also happen that there is no space left for the first note due to a scrollbar
1215 // then we also jump into, so we move the current one up and the next one down
1216 if ( (lSpaceUsed
<= (*i
)->VirtualPos().Y()) || (i
==aVisiblePostItList
.begin()))
1218 // we have space left, so let's move the current one up
1219 if ( ((*i
)->VirtualPos().Y()- lTranslatePos
- GetSpaceBetween()) > lTopBorder
)
1221 if ((*aNextPostIt
)->IsFollow())
1222 (*i
)->TranslateTopPosition(-1*(lTranslatePos
+ANCHORLINE_WIDTH
));
1224 (*i
)->TranslateTopPosition(-1*(lTranslatePos
+GetSpaceBetween()));
1228 tools::Long lMoveUp
= (*i
)->VirtualPos().Y() - lTopBorder
;
1229 (*i
)->TranslateTopPosition(-1* lMoveUp
);
1230 if ((*aNextPostIt
)->IsFollow())
1231 (*aNextPostIt
)->TranslateTopPosition( (lTranslatePos
+ANCHORLINE_WIDTH
) - lMoveUp
);
1233 (*aNextPostIt
)->TranslateTopPosition( (lTranslatePos
+GetSpaceBetween()) - lMoveUp
);
1238 // no space left, left move the next one down
1239 if ((*aNextPostIt
)->IsFollow())
1240 (*aNextPostIt
)->TranslateTopPosition(lTranslatePos
+ANCHORLINE_WIDTH
);
1242 (*aNextPostIt
)->TranslateTopPosition(lTranslatePos
+GetSpaceBetween());
1247 // the first one could overlap the topborder instead of a second note
1248 if (i
==aVisiblePostItList
.begin())
1250 tools::Long lMoveDown
= lTopBorder
- (*i
)->VirtualPos().Y();
1254 (*i
)->TranslateTopPosition( lMoveDown
);
1258 if ( (*aNextPostIt
)->IsFollow() )
1259 lSpaceUsed
+= (*i
)->VirtualSize().Height() + ANCHORLINE_WIDTH
;
1261 lSpaceUsed
+= (*i
)->VirtualSize().Height() + GetSpaceBetween();
1265 //(*i) is the last visible item
1266 auto aPrevPostIt
= i
;
1268 lTranslatePos
= ( (*aPrevPostIt
)->VirtualPos().Y() + (*aPrevPostIt
)->VirtualSize().Height() ) - (*i
)->VirtualPos().Y();
1269 if (lTranslatePos
> 0)
1272 if ( ((*i
)->VirtualPos().Y()+ (*i
)->VirtualSize().Height()+lTranslatePos
) < lBottomBorder
)
1274 if ( (*i
)->IsFollow() )
1275 (*i
)->TranslateTopPosition(lTranslatePos
+ANCHORLINE_WIDTH
);
1277 (*i
)->TranslateTopPosition(lTranslatePos
+GetSpaceBetween());
1281 (*i
)->TranslateTopPosition(lBottomBorder
- ((*i
)->VirtualPos().Y()+ (*i
)->VirtualSize().Height()) );
1286 // note does not overlap, but we might be over the lower border
1287 // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border
1288 if (!bScrollbars
&& ((*i
)->VirtualPos().Y()+ (*i
)->VirtualSize().Height() > lBottomBorder
) )
1291 (*i
)->TranslateTopPosition(lBottomBorder
- ((*i
)->VirtualPos().Y()+ (*i
)->VirtualSize().Height()));
1296 // security check so we don't loop forever
1297 if (loop
>MAX_LOOP_COUNT
)
1299 OSL_FAIL("PostItMgr::Layout(): We are looping forever");
1306 // only one left, make sure it is not hidden at the top or bottom
1307 auto i
= aVisiblePostItList
.begin();
1308 lTranslatePos
= lTopBorder
- (*i
)->VirtualPos().Y();
1309 if (lTranslatePos
>0)
1311 (*i
)->TranslateTopPosition(lTranslatePos
+GetSpaceBetween());
1313 lTranslatePos
= lBottomBorder
- ((*i
)->VirtualPos().Y()+ (*i
)->VirtualSize().Height());
1314 if (lTranslatePos
<0)
1316 (*i
)->TranslateTopPosition(lTranslatePos
);
1322 void SwPostItMgr::AddPostIts(const bool bCheckExistence
, const bool bFocus
)
1324 const bool bEmpty
= mvPostItFields
.empty();
1325 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
1326 SwFieldType
* pType
= mpView
->GetDocShell()->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit
, OUString(),false);
1327 std::vector
<SwFormatField
*> vFormatFields
;
1328 pType
->CollectPostIts(vFormatFields
, rIDRA
, mpWrtShell
->GetLayout()->IsHideRedlines());
1329 for(auto pFormatField
: vFormatFields
)
1330 InsertItem(pFormatField
, bCheckExistence
, bFocus
);
1331 // if we just added the first one we have to update the view for centering
1332 if (bEmpty
&& !mvPostItFields
.empty())
1336 void SwPostItMgr::RemoveSidebarWin()
1338 for (auto& postItField
: mvPostItFields
)
1340 EndListening( *const_cast<SfxBroadcaster
*>(postItField
->GetBroadcaster()) );
1341 postItField
->mpPostIt
.disposeAndClear();
1342 postItField
.reset();
1344 mvPostItFields
.clear();
1346 // all postits removed, no items should be left in pages
1347 PreparePageContainer();
1355 virtual bool operator()(const SwFormatField
* pField
) const = 0;
1356 virtual ~FilterFunctor() {}
1359 class IsPostitField
: public FilterFunctor
1362 bool operator()(const SwFormatField
* pField
) const override
1364 return pField
->GetField()->GetTyp()->Which() == SwFieldIds::Postit
;
1368 class IsPostitFieldWithAuthorOf
: public FilterFunctor
1372 explicit IsPostitFieldWithAuthorOf(OUString aAuthor
)
1373 : m_sAuthor(std::move(aAuthor
))
1376 bool operator()(const SwFormatField
* pField
) const override
1378 if (pField
->GetField()->GetTyp()->Which() != SwFieldIds::Postit
)
1380 return static_cast<const SwPostItField
*>(pField
->GetField())->GetPar1() == m_sAuthor
;
1384 class IsPostitFieldWithPostitId
: public FilterFunctor
1386 sal_uInt32 m_nPostItId
;
1388 explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId
)
1389 : m_nPostItId(nPostItId
)
1392 bool operator()(const SwFormatField
* pField
) const override
1394 if (pField
->GetField()->GetTyp()->Which() != SwFieldIds::Postit
)
1396 return static_cast<const SwPostItField
*>(pField
->GetField())->GetPostItId() == m_nPostItId
;
1400 class IsFieldNotDeleted
: public FilterFunctor
1403 IDocumentRedlineAccess
const& m_rIDRA
;
1404 FilterFunctor
const& m_rNext
;
1407 IsFieldNotDeleted(IDocumentRedlineAccess
const& rIDRA
,
1408 const FilterFunctor
& rNext
)
1413 bool operator()(const SwFormatField
* pField
) const override
1415 if (!m_rNext(pField
))
1417 if (!pField
->GetTextField())
1419 return !sw::IsFieldDeletedInModel(m_rIDRA
, *pField
->GetTextField());
1423 //Manages the passed in vector by automatically removing entries if they are deleted
1424 //and automatically adding entries if they appear in the document and match the
1427 //This will completely refill in the case of a "anonymous" NULL pField stating
1428 //rather unhelpfully that "something changed" so you may process the same
1429 //Fields more than once.
1430 class FieldDocWatchingStack
: public SfxListener
1432 std::vector
<std::unique_ptr
<SwSidebarItem
>>& m_aSidebarItems
;
1433 std::vector
<const SwFormatField
*> m_aFormatFields
;
1434 SwDocShell
& m_rDocShell
;
1435 FilterFunctor
& m_rFilter
;
1437 virtual void Notify(SfxBroadcaster
&, const SfxHint
& rHint
) override
1439 const SwFormatFieldHint
* pHint
= dynamic_cast<const SwFormatFieldHint
*>(&rHint
);
1443 bool bAllInvalidated
= false;
1444 if (pHint
->Which() == SwFormatFieldHintWhich::REMOVED
)
1446 const SwFormatField
* pField
= pHint
->GetField();
1447 bAllInvalidated
= pField
== nullptr;
1448 if (!bAllInvalidated
&& m_rFilter(pField
))
1450 EndListening(const_cast<SwFormatField
&>(*pField
));
1451 m_aFormatFields
.erase(std::remove(m_aFormatFields
.begin(), m_aFormatFields
.end(), pField
), m_aFormatFields
.end());
1454 else if (pHint
->Which() == SwFormatFieldHintWhich::INSERTED
)
1456 const SwFormatField
* pField
= pHint
->GetField();
1457 bAllInvalidated
= pField
== nullptr;
1458 if (!bAllInvalidated
&& m_rFilter(pField
))
1460 StartListening(const_cast<SwFormatField
&>(*pField
));
1461 m_aFormatFields
.push_back(pField
);
1465 if (bAllInvalidated
)
1472 FieldDocWatchingStack(std::vector
<std::unique_ptr
<SwSidebarItem
>>& in
, SwDocShell
&rDocShell
, FilterFunctor
& rFilter
)
1473 : m_aSidebarItems(in
)
1474 , m_rDocShell(rDocShell
)
1475 , m_rFilter(rFilter
)
1478 StartListening(m_rDocShell
);
1482 EndListeningToAllFields();
1483 m_aFormatFields
.clear();
1484 m_aFormatFields
.reserve(m_aSidebarItems
.size());
1485 for (auto const& p
: m_aSidebarItems
)
1487 const SwFormatField
& rField
= p
->GetFormatField();
1488 if (!m_rFilter(&rField
))
1490 StartListening(const_cast<SwFormatField
&>(rField
));
1491 m_aFormatFields
.push_back(&rField
);
1494 void EndListeningToAllFields()
1496 for (auto const& pField
: m_aFormatFields
)
1498 EndListening(const_cast<SwFormatField
&>(*pField
));
1501 virtual ~FieldDocWatchingStack() override
1503 EndListeningToAllFields();
1504 EndListening(m_rDocShell
);
1506 const SwFormatField
* pop()
1508 if (m_aFormatFields
.empty())
1510 const SwFormatField
* p
= m_aFormatFields
.back();
1511 EndListening(const_cast<SwFormatField
&>(*p
));
1512 m_aFormatFields
.pop_back();
1519 // copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well
1520 // RemoveItem will clean up the core field and visible postit if necessary
1521 // we cannot just delete everything as before, as postits could move into change tracking
1522 void SwPostItMgr::Delete(const OUString
& rAuthor
)
1524 mpWrtShell
->StartAllAction();
1525 if (HasActiveSidebarWin() && (GetActiveSidebarWin()->GetAuthor() == rAuthor
))
1527 SetActiveSidebarWin(nullptr);
1529 SwRewriter aRewriter
;
1530 aRewriter
.AddRule(UndoArg1
, SwResId(STR_DELETE_AUTHOR_NOTES
) + rAuthor
);
1531 mpWrtShell
->StartUndo( SwUndoId::DELETE
, &aRewriter
);
1533 IsPostitFieldWithAuthorOf
aFilter(rAuthor
);
1534 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
1535 IsFieldNotDeleted
aFilter2(rIDRA
, aFilter
);
1536 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(), aFilter2
);
1537 while (const SwFormatField
* pField
= aStack
.pop())
1539 if (mpWrtShell
->GotoField(*pField
))
1540 mpWrtShell
->DelRight();
1542 mpWrtShell
->EndUndo();
1544 mpWrtShell
->EndAllAction();
1550 void SwPostItMgr::Delete(sal_uInt32 nPostItId
)
1552 mpWrtShell
->StartAllAction();
1553 if (HasActiveSidebarWin() &&
1554 mpActivePostIt
->GetPostItField()->GetPostItId() == nPostItId
)
1556 SetActiveSidebarWin(nullptr);
1558 SwRewriter aRewriter
;
1559 aRewriter
.AddRule(UndoArg1
, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT
));
1560 mpWrtShell
->StartUndo( SwUndoId::DELETE
, &aRewriter
);
1562 IsPostitFieldWithPostitId
aFilter(nPostItId
);
1563 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
1564 IsFieldNotDeleted
aFilter2(rIDRA
, aFilter
);
1565 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(), aFilter2
);
1566 const SwFormatField
* pField
= aStack
.pop();
1567 if (pField
&& mpWrtShell
->GotoField(*pField
))
1568 mpWrtShell
->DelRight();
1569 mpWrtShell
->EndUndo();
1571 mpWrtShell
->EndAllAction();
1577 void SwPostItMgr::DeleteCommentThread(sal_uInt32 nPostItId
)
1579 mpWrtShell
->StartAllAction();
1581 SwRewriter aRewriter
;
1582 aRewriter
.AddRule(UndoArg1
, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT
));
1584 // We have no undo ID at the moment.
1586 IsPostitFieldWithPostitId
aFilter(nPostItId
);
1587 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(), aFilter
);
1588 const SwFormatField
* pField
= aStack
.pop();
1589 // pField now contains our AnnotationWin object
1591 SwAnnotationWin
* pWin
= GetSidebarWin(pField
);
1592 pWin
->DeleteThread();
1595 mpWrtShell
->EndAllAction();
1601 void SwPostItMgr::ToggleResolved(sal_uInt32 nPostItId
)
1603 mpWrtShell
->StartAllAction();
1605 SwRewriter aRewriter
;
1606 aRewriter
.AddRule(UndoArg1
, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT
));
1608 // We have no undo ID at the moment.
1610 IsPostitFieldWithPostitId
aFilter(nPostItId
);
1611 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(), aFilter
);
1612 const SwFormatField
* pField
= aStack
.pop();
1613 // pField now contains our AnnotationWin object
1615 SwAnnotationWin
* pWin
= GetSidebarWin(pField
);
1616 pWin
->ToggleResolved();
1620 mpWrtShell
->EndAllAction();
1626 void SwPostItMgr::ToggleResolvedForThread(sal_uInt32 nPostItId
)
1628 mpWrtShell
->StartAllAction();
1630 SwRewriter aRewriter
;
1631 aRewriter
.AddRule(UndoArg1
, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT
));
1633 // We have no undo ID at the moment.
1635 IsPostitFieldWithPostitId
aFilter(nPostItId
);
1636 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(), aFilter
);
1637 const SwFormatField
* pField
= aStack
.pop();
1638 // pField now contains our AnnotationWin object
1640 SwAnnotationWin
* pWin
= GetSidebarWin(pField
);
1641 pWin
->ToggleResolvedForThread();
1645 mpWrtShell
->EndAllAction();
1652 void SwPostItMgr::Delete()
1654 mpWrtShell
->StartAllAction();
1655 SetActiveSidebarWin(nullptr);
1656 SwRewriter aRewriter
;
1657 aRewriter
.AddRule(UndoArg1
, SwResId(STR_DELETE_ALL_NOTES
) );
1658 mpWrtShell
->StartUndo( SwUndoId::DELETE
, &aRewriter
);
1660 IsPostitField aFilter
;
1661 IDocumentRedlineAccess
const& rIDRA(mpWrtShell
->getIDocumentRedlineAccess());
1662 IsFieldNotDeleted
aFilter2(rIDRA
, aFilter
);
1663 FieldDocWatchingStack
aStack(mvPostItFields
, *mpView
->GetDocShell(),
1665 while (const SwFormatField
* pField
= aStack
.pop())
1667 if (mpWrtShell
->GotoField(*pField
))
1668 mpWrtShell
->DelRight();
1671 mpWrtShell
->EndUndo();
1673 mpWrtShell
->EndAllAction();
1679 void SwPostItMgr::ExecuteFormatAllDialog(SwView
& rView
)
1681 if (mvPostItFields
.empty())
1683 sw::annotation::SwAnnotationWin
*pOrigActiveWin
= GetActiveSidebarWin();
1684 sw::annotation::SwAnnotationWin
*pWin
= pOrigActiveWin
;
1687 for (auto const& postItField
: mvPostItFields
)
1689 pWin
= postItField
->mpPostIt
;
1696 SetActiveSidebarWin(pWin
);
1697 OutlinerView
* pOLV
= pWin
->GetOutlinerView();
1698 SfxItemSet
aEditAttr(pOLV
->GetAttribs());
1699 SfxItemPool
* pPool(SwAnnotationShell::GetAnnotationPool(rView
));
1700 SfxItemSetFixed
<XATTR_FILLSTYLE
, XATTR_FILLCOLOR
, EE_ITEMS_START
, EE_ITEMS_END
> aDlgAttr(*pPool
);
1701 aDlgAttr
.Put(aEditAttr
);
1702 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
1703 ScopedVclPtr
<SfxAbstractTabDialog
> pDlg(pFact
->CreateSwCharDlg(rView
.GetFrameWeld(), rView
, aDlgAttr
, SwCharDlgMode::Ann
));
1704 sal_uInt16 nRet
= pDlg
->Execute();
1707 aDlgAttr
.Put(*pDlg
->GetOutputItemSet());
1708 FormatAll(aDlgAttr
);
1710 pDlg
.disposeAndClear();
1711 SetActiveSidebarWin(pOrigActiveWin
);
1714 void SwPostItMgr::FormatAll(const SfxItemSet
&rNewAttr
)
1716 mpWrtShell
->StartAllAction();
1717 SwRewriter aRewriter
;
1718 aRewriter
.AddRule(UndoArg1
, SwResId(STR_FORMAT_ALL_NOTES
) );
1719 mpWrtShell
->StartUndo( SwUndoId::INSATTR
, &aRewriter
);
1721 for (auto const& postItField
: mvPostItFields
)
1723 if (!postItField
->mpPostIt
)
1725 OutlinerView
* pOLV
= postItField
->mpPostIt
->GetOutlinerView();
1726 //save old selection
1727 ESelection
aOrigSel(pOLV
->GetSelection());
1729 Outliner
*pOutliner
= pOLV
->GetOutliner();
1732 sal_Int32 nParaCount
= pOutliner
->GetParagraphCount();
1734 pOLV
->SelectRange(0, nParaCount
);
1736 //set new char properties
1737 pOLV
->SetAttribs(rNewAttr
);
1738 //restore old selection
1739 pOLV
->SetSelection(aOrigSel
);
1740 // tdf#91596 store updated formatting in SwField
1741 postItField
->mpPostIt
->UpdateData();
1744 mpWrtShell
->EndUndo();
1746 mpWrtShell
->EndAllAction();
1752 void SwPostItMgr::Hide( std::u16string_view rAuthor
)
1754 for (auto const& postItField
: mvPostItFields
)
1756 if ( postItField
->mpPostIt
&& (postItField
->mpPostIt
->GetAuthor() == rAuthor
) )
1758 postItField
->mbShow
= false;
1759 postItField
->mpPostIt
->HideNote();
1766 void SwPostItMgr::Hide()
1768 for (auto const& postItField
: mvPostItFields
)
1770 postItField
->mbShow
= false;
1771 if (postItField
->mpPostIt
)
1772 postItField
->mpPostIt
->HideNote();
1776 void SwPostItMgr::Show()
1778 for (auto const& postItField
: mvPostItFields
)
1780 postItField
->mbShow
= true;
1785 SwAnnotationWin
* SwPostItMgr::GetSidebarWin( const SfxBroadcaster
* pBroadcaster
) const
1787 for (auto const& postItField
: mvPostItFields
)
1789 if ( postItField
->GetBroadcaster() == pBroadcaster
)
1790 return postItField
->mpPostIt
;
1795 sw::annotation::SwAnnotationWin
* SwPostItMgr::GetAnnotationWin(const SwPostItField
* pField
) const
1797 for (auto const& postItField
: mvPostItFields
)
1799 if ( postItField
->GetFormatField().GetField() == pField
)
1800 return postItField
->mpPostIt
.get();
1805 sw::annotation::SwAnnotationWin
* SwPostItMgr::GetAnnotationWin(const sal_uInt32 nPostItId
) const
1807 for (auto const& postItField
: mvPostItFields
)
1809 if ( static_cast<const SwPostItField
*>(postItField
->GetFormatField().GetField())->GetPostItId() == nPostItId
)
1810 return postItField
->mpPostIt
.get();
1815 SwPostItField
* SwPostItMgr::GetLatestPostItField()
1817 return static_cast<SwPostItField
*>(mvPostItFields
.back()->GetFormatField().GetField());
1820 SwAnnotationWin
* SwPostItMgr::GetNextPostIt( sal_uInt16 aDirection
,
1821 SwAnnotationWin
* aPostIt
)
1823 if (mvPostItFields
.size()>1)
1825 auto i
= std::find_if(mvPostItFields
.begin(), mvPostItFields
.end(),
1826 [&aPostIt
](const std::unique_ptr
<SwSidebarItem
>& pField
) { return pField
->mpPostIt
== aPostIt
; });
1827 if (i
== mvPostItFields
.end())
1830 auto iNextPostIt
= i
;
1831 if (aDirection
== KEY_PAGEUP
)
1833 if ( iNextPostIt
== mvPostItFields
.begin() )
1842 if ( iNextPostIt
== mvPostItFields
.end() )
1847 // lets quit, we are back at the beginning
1848 if ( (*iNextPostIt
)->mpPostIt
== aPostIt
)
1850 return (*iNextPostIt
)->mpPostIt
;
1856 tools::Long
SwPostItMgr::GetNextBorder()
1858 for (auto const& pPage
: mPages
)
1860 for(auto b
= pPage
->mvSidebarItems
.begin(); b
!= pPage
->mvSidebarItems
.end(); ++b
)
1862 if ((*b
)->mpPostIt
== mpActivePostIt
)
1866 bool bFollow
= (aNext
!= pPage
->mvSidebarItems
.end()) && (*aNext
)->mpPostIt
->IsFollow();
1867 if ( pPage
->bScrollbar
|| bFollow
)
1873 //if this is the last item, return the bottom border otherwise the next item
1874 if (aNext
== pPage
->mvSidebarItems
.end())
1875 return mpEditWin
->LogicToPixel(Point(0,pPage
->mPageRect
.Bottom())).Y() - GetSpaceBetween();
1877 return (*aNext
)->mpPostIt
->GetPosPixel().Y() - GetSpaceBetween();
1883 OSL_FAIL("SwPostItMgr::GetNextBorder(): We have to find a next border here");
1887 void SwPostItMgr::SetShadowState(const SwPostItField
* pField
,bool bCursor
)
1891 if (pField
!=mShadowState
.mpShadowField
)
1893 if (mShadowState
.mpShadowField
)
1895 // reset old one if still alive
1896 // TODO: does not work properly if mouse and cursor was set
1897 sw::annotation::SwAnnotationWin
* pOldPostIt
=
1898 GetAnnotationWin(mShadowState
.mpShadowField
);
1899 if (pOldPostIt
&& pOldPostIt
->Shadow() && (pOldPostIt
->Shadow()->GetShadowState() != SS_EDIT
))
1900 pOldPostIt
->SetViewState(ViewState::NORMAL
);
1902 //set new one, if it is not currently edited
1903 sw::annotation::SwAnnotationWin
* pNewPostIt
= GetAnnotationWin(pField
);
1904 if (pNewPostIt
&& pNewPostIt
->Shadow() && (pNewPostIt
->Shadow()->GetShadowState() != SS_EDIT
))
1906 pNewPostIt
->SetViewState(ViewState::VIEW
);
1907 //remember our new field
1908 mShadowState
.mpShadowField
= pField
;
1909 mShadowState
.bCursor
= false;
1910 mShadowState
.bMouse
= false;
1914 mShadowState
.bCursor
= true;
1916 mShadowState
.bMouse
= true;
1920 if (mShadowState
.mpShadowField
)
1923 mShadowState
.bCursor
= false;
1925 mShadowState
.bMouse
= false;
1926 if (!mShadowState
.bCursor
&& !mShadowState
.bMouse
)
1928 // reset old one if still alive
1929 sw::annotation::SwAnnotationWin
* pOldPostIt
= GetAnnotationWin(mShadowState
.mpShadowField
);
1930 if (pOldPostIt
&& pOldPostIt
->Shadow() && (pOldPostIt
->Shadow()->GetShadowState() != SS_EDIT
))
1932 pOldPostIt
->SetViewState(ViewState::NORMAL
);
1933 mShadowState
.mpShadowField
= nullptr;
1940 void SwPostItMgr::PrepareView(bool bIgnoreCount
)
1942 if (!HasNotes() || bIgnoreCount
)
1944 mpWrtShell
->StartAllAction();
1945 SwRootFrame
* pLayout
= mpWrtShell
->GetLayout();
1947 SwPostItHelper::setSidebarChanged( pLayout
,
1948 mpWrtShell
->getIDocumentSettingAccess().get( DocumentSettingId::BROWSE_MODE
) );
1949 mpWrtShell
->EndAllAction();
1953 bool SwPostItMgr::ShowScrollbar(const tools::ULong aPage
) const
1955 if (mPages
.size() > aPage
-1)
1956 return (mPages
[aPage
-1]->bScrollbar
&& !mbWaitingForCalcRects
);
1961 bool SwPostItMgr::IsHit(const Point
&aPointPixel
)
1963 if (HasNotes() && ShowNotes())
1965 const Point aPoint
= mpEditWin
->PixelToLogic(aPointPixel
);
1966 const SwRootFrame
* pLayout
= mpWrtShell
->GetLayout();
1968 const tools::ULong nPageNum
= SwPostItHelper::getPageInfo( aPageFrame
, pLayout
, aPoint
);
1971 tools::Rectangle aRect
;
1972 OSL_ENSURE(mPages
.size()>nPageNum
-1,"SwPostitMgr:: page container size wrong");
1973 aRect
= mPages
[nPageNum
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
1974 ? tools::Rectangle(Point(aPageFrame
.Left()-GetSidebarWidth()-GetSidebarBorderWidth(),aPageFrame
.Top()),Size(GetSidebarWidth(),aPageFrame
.Height()))
1975 : tools::Rectangle( Point(aPageFrame
.Right()+GetSidebarBorderWidth(),aPageFrame
.Top()) , Size(GetSidebarWidth(),aPageFrame
.Height()));
1976 if (aRect
.Contains(aPoint
))
1978 // we hit the note's sidebar
1979 // lets now test for the arrow area
1980 if (mPages
[nPageNum
-1]->bScrollbar
)
1981 return ScrollbarHit(nPageNum
,aPoint
);
1990 vcl::Window
* SwPostItMgr::IsHitSidebarWindow(const Point
& rPointLogic
)
1992 vcl::Window
* pRet
= nullptr;
1994 if (HasNotes() && ShowNotes())
1996 bool bEnableMapMode
= !mpEditWin
->IsMapModeEnabled();
1998 mpEditWin
->EnableMapMode();
2000 for (const std::unique_ptr
<SwSidebarItem
>& pItem
: mvPostItFields
)
2002 SwAnnotationWin
* pPostIt
= pItem
->mpPostIt
;
2006 if (pPostIt
->IsHitWindow(rPointLogic
))
2014 mpEditWin
->EnableMapMode(false);
2020 tools::Rectangle
SwPostItMgr::GetBottomScrollRect(const tools::ULong aPage
) const
2022 SwRect aPageRect
= mPages
[aPage
-1]->mPageRect
;
2023 Point aPointBottom
= mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
2024 ? Point(aPageRect
.Left() - GetSidebarWidth() - GetSidebarBorderWidth() + mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Bottom()- mpEditWin
->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2025 : Point(aPageRect
.Right() + GetSidebarBorderWidth() + mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Bottom()- mpEditWin
->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2026 Size
aSize(GetSidebarWidth() - mpEditWin
->PixelToLogic(Size(4,0)).Width(), mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2027 return tools::Rectangle(aPointBottom
,aSize
);
2030 tools::Rectangle
SwPostItMgr::GetTopScrollRect(const tools::ULong aPage
) const
2032 SwRect aPageRect
= mPages
[aPage
-1]->mPageRect
;
2033 Point aPointTop
= mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
2034 ? Point(aPageRect
.Left() - GetSidebarWidth() -GetSidebarBorderWidth()+ mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Top() + mpEditWin
->PixelToLogic(Size(0,2)).Height())
2035 : Point(aPageRect
.Right() + GetSidebarBorderWidth() + mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Top() + mpEditWin
->PixelToLogic(Size(0,2)).Height());
2036 Size
aSize(GetSidebarWidth() - mpEditWin
->PixelToLogic(Size(4,0)).Width(), mpEditWin
->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2037 return tools::Rectangle(aPointTop
,aSize
);
2040 //IMPORTANT: if you change the rects here, also change SwPageFrame::PaintNotesSidebar()
2041 bool SwPostItMgr::ScrollbarHit(const tools::ULong aPage
,const Point
&aPoint
)
2043 SwRect aPageRect
= mPages
[aPage
-1]->mPageRect
;
2044 Point aPointBottom
= mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
2045 ? Point(aPageRect
.Left() - GetSidebarWidth()-GetSidebarBorderWidth() + mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Bottom()- mpEditWin
->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2046 : Point(aPageRect
.Right() + GetSidebarBorderWidth()+ mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Bottom()- mpEditWin
->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2048 Point aPointTop
= mPages
[aPage
-1]->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
2049 ? Point(aPageRect
.Left() - GetSidebarWidth()-GetSidebarBorderWidth()+ mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Top() + mpEditWin
->PixelToLogic(Size(0,2)).Height())
2050 : Point(aPageRect
.Right()+GetSidebarBorderWidth()+ mpEditWin
->PixelToLogic(Size(2,0)).Width(),aPageRect
.Top() + mpEditWin
->PixelToLogic(Size(0,2)).Height());
2052 tools::Rectangle
aRectBottom(GetBottomScrollRect(aPage
));
2053 tools::Rectangle
aRectTop(GetTopScrollRect(aPage
));
2055 if (aRectBottom
.Contains(aPoint
))
2057 if (aPoint
.X() < tools::Long((aPointBottom
.X() + GetSidebarWidth()/3)))
2058 Scroll( GetScrollSize(),aPage
);
2060 Scroll( -1*GetScrollSize(), aPage
);
2063 else if (aRectTop
.Contains(aPoint
))
2065 if (aPoint
.X() < tools::Long((aPointTop
.X() + GetSidebarWidth()/3*2)))
2066 Scroll(GetScrollSize(), aPage
);
2068 Scroll(-1*GetScrollSize(), aPage
);
2074 void SwPostItMgr::CorrectPositions()
2076 if ( mbWaitingForCalcRects
|| mbLayouting
|| mvPostItFields
.empty() )
2079 // find first valid note
2080 SwAnnotationWin
*pFirstPostIt
= nullptr;
2081 for (auto const& postItField
: mvPostItFields
)
2083 pFirstPostIt
= postItField
->mpPostIt
;
2088 //if we have not found a valid note, forget about it and leave
2092 // 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
2093 // check, if anchor overlay object exists.
2094 const tools::Long aAnchorX
= pFirstPostIt
->Anchor()
2095 ? mpEditWin
->LogicToPixel( Point(static_cast<tools::Long
>(pFirstPostIt
->Anchor()->GetSixthPosition().getX()),0)).X()
2097 const tools::Long aAnchorY
= pFirstPostIt
->Anchor()
2098 ? mpEditWin
->LogicToPixel( Point(0,static_cast<tools::Long
>(pFirstPostIt
->Anchor()->GetSixthPosition().getY()))).Y() + 1
2100 if (Point(aAnchorX
,aAnchorY
) == pFirstPostIt
->GetPosPixel())
2103 tools::Long aAnchorPosX
= 0;
2104 tools::Long aAnchorPosY
= 0;
2105 for (const std::unique_ptr
<SwPostItPageItem
>& pPage
: mPages
)
2107 for (auto const& item
: pPage
->mvSidebarItems
)
2109 // check, if anchor overlay object exists.
2110 if ( item
->mbShow
&& item
->mpPostIt
&& item
->mpPostIt
->Anchor() )
2112 aAnchorPosX
= pPage
->eSidebarPosition
== sw::sidebarwindows::SidebarPosition::LEFT
2113 ? mpEditWin
->LogicToPixel( Point(static_cast<tools::Long
>(item
->mpPostIt
->Anchor()->GetSeventhPosition().getX()),0)).X()
2114 : mpEditWin
->LogicToPixel( Point(static_cast<tools::Long
>(item
->mpPostIt
->Anchor()->GetSixthPosition().getX()),0)).X();
2115 aAnchorPosY
= mpEditWin
->LogicToPixel( Point(0,static_cast<tools::Long
>(item
->mpPostIt
->Anchor()->GetSixthPosition().getY()))).Y() + 1;
2116 item
->mpPostIt
->SetPosPixel(Point(aAnchorPosX
,aAnchorPosY
));
2122 bool SwPostItMgr::ShowNotes() const
2124 // we only want to see notes if Options - Writer - View - Notes is ticked
2125 return mpWrtShell
->GetViewOptions()->IsPostIts();
2128 bool SwPostItMgr::HasNotes() const
2130 return !mvPostItFields
.empty();
2133 tools::ULong
SwPostItMgr::GetSidebarWidth(bool bPx
) const
2135 bool bEnableMapMode
= !mpWrtShell
->GetOut()->IsMapModeEnabled();
2136 sal_uInt16 nZoom
= mpWrtShell
->GetViewOptions()->GetZoom();
2137 if (comphelper::LibreOfficeKit::isActive() && !bEnableMapMode
)
2139 // The output device is the tile and contains the real wanted scale factor.
2140 double fScaleX
= double(mpWrtShell
->GetOut()->GetMapMode().GetScaleX());
2141 nZoom
= fScaleX
* 100;
2143 tools::ULong aWidth
= static_cast<tools::ULong
>(nZoom
* 1.8);
2150 // The output device is the window.
2151 mpWrtShell
->GetOut()->EnableMapMode();
2152 tools::Long nRet
= mpWrtShell
->GetOut()->PixelToLogic(Size(aWidth
, 0)).Width();
2154 mpWrtShell
->GetOut()->EnableMapMode(false);
2159 tools::ULong
SwPostItMgr::GetSidebarBorderWidth(bool bPx
) const
2164 return mpWrtShell
->GetOut()->PixelToLogic(Size(2,0)).Width();
2167 Color
SwPostItMgr::GetColorDark(std::size_t aAuthorIndex
)
2169 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
2171 static const Color aArrayNormal
[] = {
2172 COL_AUTHOR1_NORMAL
, COL_AUTHOR2_NORMAL
, COL_AUTHOR3_NORMAL
,
2173 COL_AUTHOR4_NORMAL
, COL_AUTHOR5_NORMAL
, COL_AUTHOR6_NORMAL
,
2174 COL_AUTHOR7_NORMAL
, COL_AUTHOR8_NORMAL
, COL_AUTHOR9_NORMAL
};
2176 return aArrayNormal
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayNormal
)];
2182 Color
SwPostItMgr::GetColorLight(std::size_t aAuthorIndex
)
2184 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
2186 static const Color aArrayLight
[] = {
2187 COL_AUTHOR1_LIGHT
, COL_AUTHOR2_LIGHT
, COL_AUTHOR3_LIGHT
,
2188 COL_AUTHOR4_LIGHT
, COL_AUTHOR5_LIGHT
, COL_AUTHOR6_LIGHT
,
2189 COL_AUTHOR7_LIGHT
, COL_AUTHOR8_LIGHT
, COL_AUTHOR9_LIGHT
};
2191 return aArrayLight
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayLight
)];
2197 Color
SwPostItMgr::GetColorAnchor(std::size_t aAuthorIndex
)
2199 if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
2201 static const Color aArrayAnchor
[] = {
2202 COL_AUTHOR1_DARK
, COL_AUTHOR2_DARK
, COL_AUTHOR3_DARK
,
2203 COL_AUTHOR4_DARK
, COL_AUTHOR5_DARK
, COL_AUTHOR6_DARK
,
2204 COL_AUTHOR7_DARK
, COL_AUTHOR8_DARK
, COL_AUTHOR9_DARK
};
2206 return aArrayAnchor
[ aAuthorIndex
% SAL_N_ELEMENTS( aArrayAnchor
)];
2212 void SwPostItMgr::SetActiveSidebarWin( SwAnnotationWin
* p
)
2214 if ( p
== mpActivePostIt
)
2217 // we need the temp variable so we can set mpActivePostIt before we call DeactivatePostIt
2218 // therefore we get a new layout in DOCCHANGED when switching from postit to document,
2219 // otherwise, GetActivePostIt() would still hold our old postit
2220 SwAnnotationWin
* pActive
= mpActivePostIt
;
2224 pActive
->DeactivatePostIt();
2225 mShadowState
.mpShadowField
= nullptr;
2229 mpActivePostIt
->GotoPos();
2230 mpView
->AttrChangedNotify(nullptr);
2231 mpActivePostIt
->ActivatePostIt();
2235 IMPL_LINK_NOARG( SwPostItMgr
, CalcHdl
, void*, void )
2237 mnEventId
= nullptr;
2240 OSL_FAIL("Reentrance problem in Layout Manager!");
2241 mbWaitingForCalcRects
= false;
2245 // do not change order, even if it would seem so in the first place, we need the calcrects always
2246 if (CalcRects() || mbLayout
)
2253 void SwPostItMgr::Rescale()
2255 for (auto const& postItField
: mvPostItFields
)
2256 if ( postItField
->mpPostIt
)
2257 postItField
->mpPostIt
->Rescale();
2260 sal_Int32
SwPostItMgr::GetInitialAnchorDistance() const
2262 const Fraction
& f( mpEditWin
->GetMapMode().GetScaleY() );
2263 return sal_Int32(POSTIT_INITIAL_ANCHOR_DISTANCE
* f
);
2266 sal_Int32
SwPostItMgr::GetSpaceBetween() const
2268 const Fraction
& f( mpEditWin
->GetMapMode().GetScaleY() );
2269 return sal_Int32(POSTIT_SPACE_BETWEEN
* f
);
2272 sal_Int32
SwPostItMgr::GetScrollSize() const
2274 const Fraction
& f( mpEditWin
->GetMapMode().GetScaleY() );
2275 return sal_Int32((POSTIT_SPACE_BETWEEN
+ POSTIT_MINIMUMSIZE_WITH_META
) * f
);
2278 sal_Int32
SwPostItMgr::GetMinimumSizeWithMeta() const
2280 const Fraction
& f( mpEditWin
->GetMapMode().GetScaleY() );
2281 return sal_Int32(POSTIT_MINIMUMSIZE_WITH_META
* f
);
2284 sal_Int32
SwPostItMgr::GetSidebarScrollerHeight() const
2286 const Fraction
& f( mpEditWin
->GetMapMode().GetScaleY() );
2287 return sal_Int32(POSTIT_SCROLL_SIDEBAR_HEIGHT
* f
);
2290 void SwPostItMgr::SetSpellChecking()
2292 for (auto const& postItField
: mvPostItFields
)
2293 if ( postItField
->mpPostIt
)
2294 postItField
->mpPostIt
->SetSpellChecking();
2297 void SwPostItMgr::SetReadOnlyState()
2299 for (auto const& postItField
: mvPostItFields
)
2300 if ( postItField
->mpPostIt
)
2301 postItField
->mpPostIt
->SetReadonly( mbReadOnly
);
2304 void SwPostItMgr::CheckMetaText()
2306 for (auto const& postItField
: mvPostItFields
)
2307 if ( postItField
->mpPostIt
)
2308 postItField
->mpPostIt
->CheckMetaText();
2312 sal_uInt16
SwPostItMgr::Replace(SvxSearchItem
const * pItem
)
2314 SwAnnotationWin
* pWin
= GetActiveSidebarWin();
2315 sal_uInt16 aResult
= pWin
->GetOutlinerView()->StartSearchAndReplace( *pItem
);
2317 SetActiveSidebarWin(nullptr);
2321 sal_uInt16
SwPostItMgr::FinishSearchReplace(const i18nutil::SearchOptions2
& rSearchOptions
, bool bSrchForward
)
2323 SwAnnotationWin
* pWin
= GetActiveSidebarWin();
2324 SvxSearchItem
aItem(SID_SEARCH_ITEM
);
2325 aItem
.SetSearchOptions(rSearchOptions
);
2326 aItem
.SetBackward(!bSrchForward
);
2327 sal_uInt16 aResult
= pWin
->GetOutlinerView()->StartSearchAndReplace( aItem
);
2329 SetActiveSidebarWin(nullptr);
2333 sal_uInt16
SwPostItMgr::SearchReplace(const SwFormatField
&pField
, const i18nutil::SearchOptions2
& rSearchOptions
, bool bSrchForward
)
2335 sal_uInt16 aResult
= 0;
2336 SwAnnotationWin
* pWin
= GetSidebarWin(&pField
);
2339 ESelection aOldSelection
= pWin
->GetOutlinerView()->GetSelection();
2341 pWin
->GetOutlinerView()->SetSelection(ESelection(0,0,0,0));
2343 pWin
->GetOutlinerView()->SetSelection(
2344 ESelection(EE_PARA_MAX_COUNT
,EE_TEXTPOS_MAX_COUNT
,EE_PARA_MAX_COUNT
,EE_TEXTPOS_MAX_COUNT
));
2345 SvxSearchItem
aItem(SID_SEARCH_ITEM
);
2346 aItem
.SetSearchOptions(rSearchOptions
);
2347 aItem
.SetBackward(!bSrchForward
);
2348 aResult
= pWin
->GetOutlinerView()->StartSearchAndReplace( aItem
);
2350 pWin
->GetOutlinerView()->SetSelection(aOldSelection
);
2353 SetActiveSidebarWin(pWin
);
2360 void SwPostItMgr::AssureStdModeAtShell()
2362 // deselect any drawing or frame and leave editing mode
2363 SdrView
* pSdrView
= mpWrtShell
->GetDrawView();
2364 if ( pSdrView
&& pSdrView
->IsTextEdit() )
2366 bool bLockView
= mpWrtShell
->IsViewLocked();
2367 mpWrtShell
->LockView( true );
2368 mpWrtShell
->EndTextEdit();
2369 mpWrtShell
->LockView( bLockView
);
2372 if( mpWrtShell
->IsSelFrameMode() || mpWrtShell
->IsObjSelected())
2374 mpWrtShell
->UnSelectFrame();
2375 mpWrtShell
->LeaveSelFrameMode();
2376 mpWrtShell
->GetView().LeaveDrawCreate();
2377 mpWrtShell
->EnterStdMode();
2379 mpWrtShell
->DrawSelChanged();
2380 mpView
->StopShellTimer();
2384 bool SwPostItMgr::HasActiveSidebarWin() const
2386 return mpActivePostIt
!= nullptr;
2389 bool SwPostItMgr::HasActiveAnnotationWin() const
2391 return HasActiveSidebarWin() &&
2392 mpActivePostIt
!= nullptr;
2395 void SwPostItMgr::GrabFocusOnActiveSidebarWin()
2397 if ( HasActiveSidebarWin() )
2399 mpActivePostIt
->GrabFocus();
2403 void SwPostItMgr::UpdateDataOnActiveSidebarWin()
2405 if ( HasActiveSidebarWin() )
2407 mpActivePostIt
->UpdateData();
2411 void SwPostItMgr::DeleteActiveSidebarWin()
2413 if ( HasActiveSidebarWin() )
2415 mpActivePostIt
->Delete();
2419 void SwPostItMgr::HideActiveSidebarWin()
2421 if ( HasActiveSidebarWin() )
2423 mpActivePostIt
->Hide();
2427 void SwPostItMgr::ToggleInsModeOnActiveSidebarWin()
2429 if ( HasActiveSidebarWin() )
2431 mpActivePostIt
->ToggleInsMode();
2435 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2436 void SwPostItMgr::ConnectSidebarWinToFrame( const SwFrame
& rFrame
,
2437 const SwFormatField
& rFormatField
,
2438 SwAnnotationWin
& rSidebarWin
)
2440 if ( mpFrameSidebarWinContainer
== nullptr )
2442 mpFrameSidebarWinContainer
.reset(new SwFrameSidebarWinContainer());
2445 const bool bInserted
= mpFrameSidebarWinContainer
->insert( rFrame
, rFormatField
, rSidebarWin
);
2447 mpWrtShell
->GetAccessibleMap() )
2449 mpWrtShell
->GetAccessibleMap()->InvalidatePosOrSize( nullptr, nullptr, &rSidebarWin
, SwRect() );
2453 void SwPostItMgr::DisconnectSidebarWinFromFrame( const SwFrame
& rFrame
,
2454 SwAnnotationWin
& rSidebarWin
)
2456 if ( mpFrameSidebarWinContainer
!= nullptr )
2458 const bool bRemoved
= mpFrameSidebarWinContainer
->remove( rFrame
, rSidebarWin
);
2460 mpWrtShell
->GetAccessibleMap() )
2462 mpWrtShell
->GetAccessibleMap()->A11yDispose( nullptr, nullptr, &rSidebarWin
);
2466 #endif // ENABLE_WASM_STRIP_ACCESSIBILITY
2468 bool SwPostItMgr::HasFrameConnectedSidebarWins( const SwFrame
& rFrame
)
2472 if ( mpFrameSidebarWinContainer
!= nullptr )
2474 bRet
= !mpFrameSidebarWinContainer
->empty( rFrame
);
2480 vcl::Window
* SwPostItMgr::GetSidebarWinForFrameByIndex( const SwFrame
& rFrame
,
2481 const sal_Int32 nIndex
)
2483 vcl::Window
* pSidebarWin( nullptr );
2485 if ( mpFrameSidebarWinContainer
!= nullptr )
2487 pSidebarWin
= mpFrameSidebarWinContainer
->get( rFrame
, nIndex
);
2493 void SwPostItMgr::GetAllSidebarWinForFrame( const SwFrame
& rFrame
,
2494 std::vector
< vcl::Window
* >* pChildren
)
2496 if ( mpFrameSidebarWinContainer
!= nullptr )
2498 mpFrameSidebarWinContainer
->getAll( rFrame
, pChildren
);
2502 void SwPostItMgr::ShowHideResolvedNotes(bool visible
) {
2503 for (auto const& pPage
: mPages
)
2505 for(auto b
= pPage
->mvSidebarItems
.begin(); b
!= pPage
->mvSidebarItems
.end(); ++b
)
2507 if ((*b
)->mpPostIt
->IsResolved())
2509 (*b
)->mpPostIt
->SetResolved(true);
2510 (*b
)->mpPostIt
->GetSidebarItem().mbShow
= visible
;
2517 void SwPostItMgr::UpdateResolvedStatus(const sw::annotation::SwAnnotationWin
* topNote
) {
2518 // Given the topmost note as an argument, scans over all notes and sets the
2519 // 'resolved' state of each descendant of the top notes to the resolved state
2521 bool resolved
= topNote
->IsResolved();
2522 for (auto const& pPage
: mPages
)
2524 for(auto b
= pPage
->mvSidebarItems
.begin(); b
!= pPage
->mvSidebarItems
.end(); ++b
)
2526 if((*b
)->mpPostIt
->GetTopReplyNote() == topNote
) {
2527 (*b
)->mpPostIt
->SetResolved(resolved
);
2533 void SwNoteProps::ImplCommit() {}
2534 void SwNoteProps::Notify( const css::uno::Sequence
< OUString
>& ) {}
2536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */