/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// distance between Anchor Y and initial note position #define POSTIT_INITIAL_ANCHOR_DISTANCE 20 //distance between two postits #define POSTIT_SPACE_BETWEEN 8 #define POSTIT_MINIMUMSIZE_WITH_META 60 #define POSTIT_SCROLL_SIDEBAR_HEIGHT 20
// if we layout more often we stop, this should never happen #define MAX_LOOP_COUNT 50
// is the anchor placed in Footnote or the Footer? if( aPosAnchorA.GetNode().FindFootnoteStartNode() || aPosAnchorA.GetNode().FindFooterStartNode() )
aAnchorAInFooter = true; if( aPosAnchorB.GetNode().FindFootnoteStartNode() || aPosAnchorB.GetNode().FindFooterStartNode() )
aAnchorBInFooter = true;
// fdo#34800 // if AnchorA is in footnote, and AnchorB isn't // we do not want to change over the position if( aAnchorAInFooter && !aAnchorBInFooter ) returnfalse; // if aAnchorA is not placed in a footnote, and aAnchorB is // force a change over elseif( !aAnchorAInFooter && aAnchorBInFooter ) returntrue; // If neither or both are in the footer, compare the positions. // Since footnotes are in Inserts section of nodes array and footers // in Autotext section, all footnotes precede any footers so no need // to check that. else return aPosAnchorA < aPosAnchorB;
}
/// Emits LOK notification about one addition/removal/change of a comment void lcl_CommentNotification(const SwView* pView, const CommentNotificationType nType, const SwAnnotationItem* pItem, const sal_uInt32 nPostItId)
{ if (!comphelper::LibreOfficeKit::isActive()) return;
if (!pItem->maLayoutInfo.mPositionFromCommentAnchor)
{ // Comments on frames: anchor position is the corner position, not the whole frame.
aSVRect.SetSize(Size(0, 0));
}
aAnnotation.put("id", pField->GetPostItId());
aAnnotation.put("parentId", pField->GetParentPostItId());
aAnnotation.put("author", pField->GetPar1().toUtf8().getStr()); // Note, for just plain text we could use "text" populated by pField->GetPar2()
aAnnotation.put("html", pWin->GetSimpleHtml());
aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false");
aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
aAnnotation.put("anchorPos", aSVRect.toString());
aAnnotation.put("textRange", sRects.getStr());
aAnnotation.put("layoutStatus", pItem->mLayoutStatus);
} if (nType == CommentNotificationType::Remove && comphelper::LibreOfficeKit::isActive())
{ // Redline author is basically the author which has made the modification rather than author of the comments // This is important to know who removed the comment
aAnnotation.put("author", SwModule::get()->GetRedlineAuthor(SwModule::get()->GetRedlineAuthor()));
}
//Manages the passed in vector by automatically removing entries if they are deleted //and automatically adding entries if they appear in the document and match the //functor. // //This will completely refill in the case of a "anonymous" NULL pField stating //rather unhelpfully that "something changed" so you may process the same //Fields more than once. class FieldDocWatchingStack : public SfxListener
{
std::vector<std::unique_ptr<SwAnnotationItem>>& m_aSidebarItems;
std::vector<const SwFormatField*> m_aFormatFields;
SwDocShell& m_rDocShell;
FilterFunctor& m_rFilter;
bool isOwnFileFormat(SfxMedium* pMedium)
{ // Assume that unsaved documents are own format return !pMedium || !pMedium->GetFilter() || pMedium->GetFilter()->IsOwnFormat();
}
//make sure we get the colour yellow always, even if not the first one of comments or redlining
SwModule::get()->GetRedlineAuthor();
// collect all PostIts and redline comments that exist after loading the document // don't check for existence for any of them, don't focus them
AddPostIts(false,false); /* this code can be used once we want redline comments in the Sidebar AddRedlineComments(false,false);
*/ // we want to receive stuff like SfxHintId::DocChanged
StartListening(*mpView->GetDocShell()); // listen to stylesheet pool to update on stylesheet rename, // as EditTextObject references styles by name.
SfxStyleSheetBasePool* pStyleSheetPool = mpView->GetDocShell()->GetStyleSheetPool(); if (pStyleSheetPool)
StartListening(*static_cast<SwDocStyleSheetPool*>(pStyleSheetPool)->GetEEStyleSheetPool()); if (!mvPostItFields.empty())
{
mbWaitingForCalcRects = true;
mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
}
}
SwPostItMgr::~SwPostItMgr()
{ if ( mnEventId )
Application::RemoveUserEvent( mnEventId ); // forget about all our Sidebar windows
RemoveSidebarWin();
EndListeningAll();
mPages.clear();
}
bool SwPostItMgr::CheckForRemovedPostIts()
{
IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); bool bRemoved = false; auto it = mvPostItFields.begin(); while(it != mvPostItFields.end())
{ if (!(*it)->UseElement(*mpWrtShell->GetLayout(), rIDRA))
{
EndListening(const_cast<SfxBroadcaster&>(*(*it)->GetBroadcaster()));
std::unique_ptr<SwAnnotationItem> p = std::move(*it);
it = mvPostItFields.erase(it); if (GetActiveSidebarWin() == p->mpPostIt)
SetActiveSidebarWin(nullptr);
p->mpPostIt.disposeAndClear();
// make sure that no deleted items remain in page lists // todo: only remove deleted ones?! if ( mvPostItFields.empty() )
{
PreparePageContainer();
PrepareView();
} else
{ // if postits are there make sure that page lists are not empty // otherwise sudden paints can cause pain (in BorderOverPageBorder)
CalcRects();
}
returntrue;
}
SwAnnotationItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus)
{ if (bCheckExistence)
{ for (autoconst& postItField : mvPostItFields)
{ if ( postItField->GetBroadcaster() == pItem ) return nullptr;
}
}
mbLayout = bFocus;
SwAnnotationItem* pAnnotationItem = nullptr; if (auto pSwFormatField = dynamic_cast< SwFormatField *>( pItem ))
{
IsPostitField isPostitField; if (!isPostitField(pSwFormatField)) return nullptr;
mvPostItFields.push_back(std::make_unique<SwAnnotationItem>(*pSwFormatField, bFocus));
pAnnotationItem = mvPostItFields.back().get();
}
assert(dynamic_cast< const SwFormatField *>( pItem ) && "Mgr::InsertItem: seems like new stuff was added");
StartListening(*pItem); return pAnnotationItem;
}
sw::annotation::SwAnnotationWin* SwPostItMgr::GetRemovedAnnotationWin( const SfxBroadcaster* pBroadcast )
{ auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
[&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; }); if (i != mvPostItFields.end())
{ return (*i)->mpPostIt;
} return nullptr;
}
void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast )
{
EndListening(*pBroadcast); auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
[&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; }); if (i != mvPostItFields.end())
{ #if ENABLE_YRS // note: (*i)->mpPostIt may be null here, if it's in hidden text - see testMissingDefaultLineColor
mpView->GetDocShell()->GetDoc()->getIDocumentState().YrsRemoveComment(
(*i)->GetAnchorPosition()); #endif
std::unique_ptr<SwAnnotationItem> p = std::move(*i); // tdf#120487 remove from list before dispose, so comment window // won't be recreated due to the entry still in the list if focus // transferring from the pPostIt triggers relayout of postits // tdf#133348 remove from list before calling SetActiveSidebarWin // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr
mvPostItFields.erase(i); if (GetActiveSidebarWin() == p->mpPostIt)
SetActiveSidebarWin(nullptr);
p->mpPostIt.disposeAndClear();
}
mbLayout = true;
PrepareView();
}
void SwPostItMgr::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{ if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
{ const SfxEventHint& rSfxEventHint = static_cast<const SfxEventHint&>(rHint); if (rSfxEventHint.GetEventId() == SfxEventHintId::SwEventLayoutFinished)
{ if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
{
mbWaitingForCalcRects = true;
mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
}
}
} elseif ( rHint.GetId() == SfxHintId::SwFormatField )
{ const SwFormatFieldHint * pFormatHint = static_cast<const SwFormatFieldHint*>(&rHint);
SwFormatField* pField = const_cast <SwFormatField*>( pFormatHint->GetField() ); switch ( pFormatHint->Which() )
{ case SwFormatFieldHintWhich::INSERTED :
{ if (!pField)
{
AddPostIts(); break;
} // get field to be inserted from hint if ( pField->IsFieldInDoc() )
{ bool bEmpty = !HasNotes();
SwAnnotationItem* pItem = InsertItem( pField, true, false );
if (bEmpty && !mvPostItFields.empty())
PrepareView(true);
// True until the layout of this post it finishes if (pItem)
pItem->mbPendingLayout = true;
} else
{
OSL_FAIL("Inserted field not in document!" );
} break;
} case SwFormatFieldHintWhich::REMOVED: case SwFormatFieldHintWhich::REDLINED_DELETION:
{ if (mbDeleteNote)
{ if (!pField)
{ constbool bWasRemoved = CheckForRemovedPostIts(); // tdf#143643 ensure relayout on undo of insert comment if (bWasRemoved)
mbLayout = true; break;
}
this->Broadcast(rHint);
RemoveItem(pField);
// If LOK has disabled tiled annotations, emit annotation callbacks if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
{
SwPostItField* pPostItField = static_cast<SwPostItField*>(pField->GetField()); auto type = pFormatHint->Which() == SwFormatFieldHintWhich::REMOVED ? CommentNotificationType::Remove: CommentNotificationType::RedlinedDeletion;
lcl_CommentNotification(mpView, type, nullptr, pPostItField->GetPostItId());
}
} break;
} case SwFormatFieldHintWhich::FOCUS:
{ if (pFormatHint->GetView()== mpView)
Focus(rBC); break;
} case SwFormatFieldHintWhich::CHANGED: case SwFormatFieldHintWhich::RESOLVED:
{
SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC); for (autoconst& postItField : mvPostItFields)
{ if ( pFormatField == postItField->GetBroadcaster() )
{ if (postItField->mpPostIt)
{
postItField->mpPostIt->SetPostItText();
mbLayout = true;
this->Forward(rBC, rHint);
}
// If LOK has disabled tiled annotations, emit annotation callbacks if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
{ if(SwFormatFieldHintWhich::CHANGED == pFormatHint->Which())
lcl_CommentNotification(mpView, CommentNotificationType::Modify, postItField.get(), 0); else
lcl_CommentNotification(mpView, CommentNotificationType::Resolve, postItField.get(), 0);
} break;
}
} break;
}
}
} elseif ( rHint.GetId() == SfxHintId::StyleSheetModifiedExtended )
{ const SfxStyleSheetModifiedHint * pStyleHint = static_cast<const SfxStyleSheetModifiedHint*>(&rHint); for (constauto& postItField : mvPostItFields)
{ auto pField = static_cast<SwPostItField*>(postItField->GetFormatField().GetField());
pField->ChangeStyleSheetName(pStyleHint->GetOldName(), pStyleHint->GetStyleSheet());
}
} else
{
SfxHintId nId = rHint.GetId(); switch ( nId )
{ case SfxHintId::ModeChanged:
{ if ( mbReadOnly != mpView->GetDocShell()->IsReadOnly() )
{
mbReadOnly = !mbReadOnly;
SetReadOnlyState();
mbLayout = true;
} break;
} case SfxHintId::DocChanged:
{ if ( mpView->GetDocShell() == &rBC )
{ if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
{
mbWaitingForCalcRects = true;
mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
}
} break;
} case SfxHintId::LanguageChanged:
{
SetSpellChecking(); break;
} case SfxHintId::SwSplitNodeOperation:
{ // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker
mbDeleteNote = !mbDeleteNote; break;
} case SfxHintId::Dying:
{ if ( mpView->GetDocShell() != &rBC )
{ // field to be removed is the broadcaster
OSL_FAIL("Notification for removed SwFormatField was not sent!");
RemoveItem(&rBC);
} break;
} default: break;
}
}
}
for (autoconst& postItField : mvPostItFields)
{ // field to get the focus is the broadcaster if ( &rBC == postItField->GetBroadcaster() )
{ if (postItField->mpPostIt)
{ if (postItField->mpPostIt->IsResolved() &&
!mpWrtShell->GetViewOptions()->IsResolvedPostIts())
{
SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_RESOLVED_NOTES);
mpView->ExecViewOptions(aRequest);
}
postItField->mpPostIt->GrabFocus();
MakeVisible(postItField->mpPostIt);
} else
{ // when the layout algorithm starts, this postit is created and receives focus
postItField->mbFocus = true;
}
}
}
}
bool SwPostItMgr::CalcRects()
{ if ( mnEventId )
{ // if CalcRects() was forced and an event is still pending: remove it // it is superfluous and also may cause reentrance problems if triggered while layouting
Application::RemoveUserEvent( mnEventId );
mnEventId = nullptr;
}
// show notes in right order in navigator //prevent Anchors during layout to overlap, e.g. when moving a frame if (mvPostItFields.size()>1 )
std::stable_sort(mvPostItFields.begin(), mvPostItFields.end(), comp_pos);
// sort the items into the right page vector, so layout can be done by page for (autoconst& pItem : mvPostItFields)
{ if( SwPostItHelper::INVISIBLE == pItem->mLayoutStatus )
{ if (pItem->mpPostIt)
pItem->mpPostIt->HideNote(); continue;
}
if( SwPostItHelper::HIDDEN == pItem->mLayoutStatus )
{ if (!mpWrtShell->GetViewOptions()->IsShowHiddenChar())
{ if (pItem->mpPostIt)
pItem->mpPostIt->HideNote(); continue;
}
}
bool SwPostItMgr::HasScrollbars() const
{ for (autoconst& postItField : mvPostItFields)
{ if (postItField->mbShow && postItField->mpPostIt && postItField->mpPostIt->HasScrollbar()) returntrue;
} returnfalse;
}
void SwPostItMgr::PreparePageContainer()
{ // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost
tools::Long lPageSize = mpWrtShell->GetNumPages();
tools::Long lContainerSize = mPages.size();
if (lContainerSize < lPageSize)
{
mPages.reserve(lPageSize); for (tools::Long i=0; i<lPageSize - lContainerSize;i++)
mPages.emplace_back( new SwPostItPageItem());
} elseif (lContainerSize > lPageSize)
{ for (int i=mPages.size()-1; i >= lPageSize;--i)
{
mPages.pop_back();
}
} // only clear the list, DO NOT delete the objects itself for (autoconst& page : mPages)
{
page->mvSidebarItems.clear(); if (mvPostItFields.empty())
page->bScrollbar = false;
}
}
VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationItem& rItem, bool& rCreated)
{
VclPtr<SwAnnotationWin> pPostIt = rItem.mpPostIt; if (!pPostIt)
{
pPostIt = rItem.GetSidebarWindow( mpView->GetEditWin(),
*this );
pPostIt->InitControls();
pPostIt->SetReadonly(mbReadOnly);
rItem.mpPostIt = pPostIt; #if ENABLE_YRS
SAL_INFO("sw.yrs", "YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt); #endif if (mpAnswer)
{ if (pPostIt->GetPostItField()->GetParentPostItId() != 0) //do we really have another note in front of this one
{
pPostIt->InitAnswer(*mpAnswer);
}
mpAnswer.reset();
}
//loop over all pages and do the layout // - create SwPostIt if necessary // - place SwPostIts on their initial position // - calculate necessary height for all PostIts together bool bUpdate = false; for (std::unique_ptr<SwPostItPageItem>& pPage : mPages)
{ // only layout if there are notes on this page if (!pPage->mvSidebarItems.empty())
{
std::vector<SwAnnotationWin*> aVisiblePostItList;
tools::ULong lNeededHeight = 0;
for (autoconst& pItem : pPage->mvSidebarItems)
{ if (pItem->mbShow)
{ bool bCreated = false;
VclPtr<SwAnnotationWin> pPostIt = GetOrCreateAnnotationWindow(*pItem, bCreated); if (bCreated)
{ // The annotation window was created for a previously existing, but not // laid out comment.
aCreatedPostIts.insert(pPostIt);
}
if (pItem->mbFocus)
{
mbLayout = true;
pPostIt->GrabFocus();
pItem->mbFocus = false;
} // only the visible postits are used for the final layout
aVisiblePostItList.push_back(pPostIt); if (bShowNotes)
lNeededHeight += pPostIt->IsFollow() ? aPostItHeight : aPostItHeight+GetSpaceBetween();
} else// we don't want to see it
{
VclPtr<SwAnnotationWin> pPostIt = pItem->mpPostIt; if (pPostIt)
pPostIt->HideNote();
}
SwFormatField* pFormatField = &(pItem->GetFormatField());
SwFormatFieldHintWhich nWhich = SwFormatFieldHintWhich::INSERTED;
this->Broadcast(SwFormatFieldHint(pFormatField, nWhich, mpView));
}
if (!aVisiblePostItList.empty() && ShowNotes())
{ bool bOldScrollbar = pPage->bScrollbar;
pPage->bScrollbar = LayoutByPage(aVisiblePostItList, pPage->mPageRect.SVRect(), lNeededHeight); if (!pPage->bScrollbar)
{
pPage->lOffset = 0;
} elseif (sal_Int32 nScrollSize = GetScrollSize())
{ //when we changed our zoom level, the offset value can be too big, so let's check for the largest possible zoom value
tools::Long aAvailableHeight = mpEditWin->LogicToPixel(Size(0,pPage->mPageRect.Height())).Height() - 2 * GetSidebarScrollerHeight();
tools::Long lOffset = -1 * nScrollSize * (aVisiblePostItList.size() - aAvailableHeight / nScrollSize); if (pPage->lOffset < lOffset)
pPage->lOffset = lOffset;
}
bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate; const tools::Long aSidebarheight = pPage->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0; /* TODO - enlarge all notes till GetNextBorder(), as we resized to average value before
*/ //let's hide the ones which overlap the page for (autoconst& visiblePostIt : aVisiblePostItList)
{ if (pPage->lOffset != 0)
visiblePostIt->TranslateTopPosition(pPage->lOffset);
bool bBottom = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y()+visiblePostIt->VirtualSize().Height())).Y() <= (pPage->mPageRect.Bottom()-aSidebarheight); bool bTop = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() >= (pPage->mPageRect.Top()+aSidebarheight); if ( bBottom && bTop )
{ // When tiled rendering, make sure that only the // view that has the comment focus emits callbacks, // so the editing view jumps to the comment, but // not the others. bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting(); if (!bTiledPainting) // No focus -> disable callbacks.
comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt->HasChildPathFocus());
visiblePostIt->ShowNote(); if (!bTiledPainting)
comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
} else
{ if (mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() < (pPage->mPageRect.Top()+aSidebarheight))
{ if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Left(),
pPage->mPageRect.Top())); elseif ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Right(),
pPage->mPageRect.Top()));
} else
{ if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Left(),
pPage->mPageRect.Bottom())); elseif ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Right(),
pPage->mPageRect.Bottom()));
}
OSL_ENSURE(pPage->bScrollbar,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true");
}
}
} else
{ for (autoconst& visiblePostIt : aVisiblePostItList)
{
visiblePostIt->SetPosAndSize();
}
for (autoconst& visiblePostIt : aVisiblePostItList)
{ if (bLoKitActive && !bTiledAnnotations)
{ if (visiblePostIt->GetSidebarItem().mbPendingLayout && visiblePostIt->GetSidebarItem().mLayoutStatus != SwPostItHelper::DELETED)
{ // Notify about a just inserted comment.
aCreatedPostIts.insert(visiblePostIt);
} elseif (visiblePostIt->IsAnchorRectChanged())
{
lcl_CommentNotification(mpView, CommentNotificationType::Modify, &visiblePostIt->GetSidebarItem(), 0);
visiblePostIt->ResetAnchorRectChanged();
}
}
// Layout for this post it finished now
visiblePostIt->GetSidebarItem().mbPendingLayout = false;
}
} else
{ if (pPage->bScrollbar)
bUpdate = true;
pPage->bScrollbar = false;
}
}
if (!bShowNotes)
{ // we do not want to see the notes anymore -> Options-Writer-View-Notes
IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess()); bool bRepair = false; for (autoconst& postItField : mvPostItFields)
{ if (!postItField->UseElement(*mpWrtShell->GetLayout(), rIDRA))
{
OSL_FAIL("PostIt is not in doc!");
bRepair = true; continue;
}
if (postItField->mpPostIt)
{
postItField->mpPostIt->HideNote(); if (postItField->mpPostIt->HasChildPathFocus())
{
SetActiveSidebarWin(nullptr);
postItField->mpPostIt->GrabFocusToDocument();
}
}
}
if ( bRepair )
CheckForRemovedPostIts();
}
// notes scrollbar is otherwise not drawn correctly for some cases // scrollbar area is enough if (bUpdate)
mpEditWin->Invalidate(); /*This is a super expensive relayout and render of the entire page*/
mbLayouting = false;
}
// Now that comments are laid out, notify about freshly laid out or just inserted comments. for (constauto& pPostIt : aCreatedPostIts)
{
lcl_CommentNotification(mpView, CommentNotificationType::Add, &pPostIt->GetSidebarItem(), 0);
}
if (bEnableMapMode)
mpEditWin->EnableMapMode(false);
}
bool SwPostItMgr::BorderOverPageBorder(tools::ULong aPage) const
{ if ( mPages[aPage-1]->mvSidebarItems.empty() )
{
OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!"); returnfalse;
}
auto aItem = mPages[aPage-1]->mvSidebarItems.end();
--aItem;
OSL_ENSURE ((*aItem)->mpPostIt,"BorderOverPageBorder: NULL postIt, should never happen"); if ((*aItem)->mpPostIt)
{ const tools::Long aSidebarheight = mPages[aPage-1]->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0; const tools::Long aEndValue = mpEditWin->PixelToLogic(Point(0,(*aItem)->mpPostIt->GetPosPixel().Y()+(*aItem)->mpPostIt->GetSizePixel().Height())).Y(); return aEndValue <= mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight;
} else returnfalse;
}
void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage)
{
assert(nPage < mPages.size()); if (nPage >= mPages.size()) return; for (autoconst& pItem : mPages[nPage]->mvSidebarItems)
{
SwAnnotationWin* pPostIt = pItem->mpPostIt; if (!pPostIt) continue;
Point aPoint(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
pPostIt->DrawForPage(pOutDev, aPoint);
}
}
void SwPostItMgr::PaintTile(OutputDevice& rRenderContext)
{ for (const std::unique_ptr<SwAnnotationItem>& pItem : mvPostItFields)
{
SwAnnotationWin* pPostIt = pItem->mpPostIt; if (!pPostIt) continue;
rRenderContext.Pop(); if (bEnableMapMode)
mpEditWin->EnableMapMode(false);
}
}
void SwPostItMgr::Scroll(const tools::Long lScroll,const tools::ULong aPage)
{
OSL_ENSURE((lScroll % GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value"); // do not scroll more than necessary up or down if ( ((mPages[aPage-1]->lOffset == 0) && (lScroll>0)) || ( BorderOverPageBorder(aPage) && (lScroll<0)) ) return;
constbool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage); constbool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage); const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height(); for (autoconst& item : mPages[aPage-1]->mvSidebarItems)
{
SwAnnotationWin* pPostIt = item->mpPostIt; // if this is an answer, we should take the normal position and not the real, slightly moved position
pPostIt->SetVirtualPosSize(pPostIt->GetPosPixel(),pPostIt->GetSizePixel());
pPostIt->TranslateTopPosition(lScroll);
Color SwPostItMgr::GetArrowColor(sal_uInt16 aDirection,tools::ULong aPage) const
{ if (ArrowEnabled(aDirection,aPage))
{ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) return COL_WHITE; else return COL_NOTES_SIDEPANE_ARROW_ENABLED;
} else
{ return COL_NOTES_SIDEPANE_ARROW_DISABLED;
}
}
bool SwPostItMgr::LayoutByPage(std::vector<SwAnnotationWin*> &aVisiblePostItList, const tools::Rectangle& rBorder, tools::Long lNeededHeight)
{ /*** General layout idea:***/ // - if we have space left, we always move the current one up, // otherwise the next one down // - first all notes are resized // - then the real layout starts
// do all necessary resizings if (nPostItListSize > 0 && lVisibleHeight < lNeededHeight)
{ // ok, now we have to really resize and adding scrollbars const tools::Long lAverageHeight = (lVisibleHeight - nPostItListSize*GetSpaceBetween()) / nPostItListSize; if (lAverageHeight<GetMinimumSizeWithMeta())
{
bScrollbars = true;
lTopBorder += GetSidebarScrollerHeight() + 10;
lBottomBorder -= (GetSidebarScrollerHeight() + 10); for (autoconst& visiblePostIt : aVisiblePostItList)
visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta()));
} else
{ for (autoconst& visiblePostIt : aVisiblePostItList)
{ if ( visiblePostIt->VirtualSize().getHeight() > lAverageHeight)
visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),lAverageHeight));
}
}
}
//start the real layout so nothing overlaps anymore if (aVisiblePostItList.size()>1)
{ int loop = 0; bool bDone = false; // if no window is moved anymore we are finished while (!bDone)
{
loop++;
bDone = true;
tools::Long lSpaceUsed = lTopBorder + GetSpaceBetween(); for(auto i = aVisiblePostItList.begin(); i != aVisiblePostItList.end() ; ++i)
{ auto aNextPostIt = i;
++aNextPostIt;
if (aNextPostIt != aVisiblePostItList.end())
{
lTranslatePos = ( (*i)->VirtualPos().Y() + (*i)->VirtualSize().Height()) - (*aNextPostIt)->VirtualPos().Y(); if (lTranslatePos > 0) // note windows overlaps the next one
{ // we are not done yet, loop at least once more
bDone = false; // if there is space left, move the current note up // it could also happen that there is no space left for the first note due to a scrollbar // then we also jump into, so we move the current one up and the next one down if ( (lSpaceUsed <= (*i)->VirtualPos().Y()) || (i==aVisiblePostItList.begin()))
{ // we have space left, so let's move the current one up if ( ((*i)->VirtualPos().Y()- lTranslatePos - GetSpaceBetween()) > lTopBorder)
{ if ((*aNextPostIt)->IsFollow())
(*i)->TranslateTopPosition(-1*(lTranslatePos+ANCHORLINE_WIDTH)); else
(*i)->TranslateTopPosition(-1*(lTranslatePos+GetSpaceBetween()));
} else
{
tools::Long lMoveUp = (*i)->VirtualPos().Y() - lTopBorder;
(*i)->TranslateTopPosition(-1* lMoveUp); if ((*aNextPostIt)->IsFollow())
(*aNextPostIt)->TranslateTopPosition( (lTranslatePos+ANCHORLINE_WIDTH) - lMoveUp); else
(*aNextPostIt)->TranslateTopPosition( (lTranslatePos+GetSpaceBetween()) - lMoveUp);
}
} else
{ // no space left, left move the next one down if ((*aNextPostIt)->IsFollow())
(*aNextPostIt)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH); else
(*aNextPostIt)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
}
} else
{ // the first one could overlap the topborder instead of a second note if (i==aVisiblePostItList.begin())
{
tools::Long lMoveDown = lTopBorder - (*i)->VirtualPos().Y(); if (lMoveDown>0)
{
bDone = false;
(*i)->TranslateTopPosition( lMoveDown);
}
}
} if ( (*aNextPostIt)->IsFollow() )
lSpaceUsed += (*i)->VirtualSize().Height() + ANCHORLINE_WIDTH; else
lSpaceUsed += (*i)->VirtualSize().Height() + GetSpaceBetween();
} else
{ //(*i) is the last visible item auto aPrevPostIt = i;
--aPrevPostIt;
lTranslatePos = ( (*aPrevPostIt)->VirtualPos().Y() + (*aPrevPostIt)->VirtualSize().Height() ) - (*i)->VirtualPos().Y(); if (lTranslatePos > 0)
{
bDone = false; if ( ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()+lTranslatePos) < lBottomBorder)
{ if ( (*i)->IsFollow() )
(*i)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH); else
(*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
} else
{
(*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()) );
}
} else
{ // note does not overlap, but we might be over the lower border // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border if (!bScrollbars && ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height() > lBottomBorder) )
{
bDone = false;
(*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()));
}
}
}
} // security check so we don't loop forever if (loop>MAX_LOOP_COUNT)
{
OSL_FAIL("PostItMgr::Layout(): We are looping forever"); break;
}
}
} else
{ // only one left, make sure it is not hidden at the top or bottom auto i = aVisiblePostItList.begin();
lTranslatePos = lTopBorder - (*i)->VirtualPos().Y(); if (lTranslatePos>0)
{
(*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
}
lTranslatePos = lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()); if (lTranslatePos<0)
{
(*i)->TranslateTopPosition(lTranslatePos);
}
} return bScrollbars;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.