Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/uibase/docvw/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 106 kB image not shown  

Quelle  PostItMgr.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <config_wasm_strip.h>

#include <boost/property_tree/json_parser.hpp>

#include <PostItMgr.hxx>
#include <postithelper.hxx>

#include <AnnotationWin.hxx>
#include "frmsidebarwincontainer.hxx"
#include <accmap.hxx>

#include <SidebarWindowsConsts.hxx>
#include "AnchorOverlayObject.hxx"
#include "ShadowOverlayObject.hxx"

#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/outdev.hxx>
#include <vcl/settings.hxx>

#include <chrdlgmodes.hxx>
#include <viewopt.hxx>
#include <view.hxx>
#include <docsh.hxx>
#include <wrtsh.hxx>
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentRedlineAccess.hxx>
#if ENABLE_YRS
#include <IDocumentState.hxx>
#endif
#include <docstyle.hxx>
#include <fldbas.hxx>
#include <fmtfld.hxx>
#include <docufld.hxx>
#include <edtwin.hxx>
#include <txtfld.hxx>
#include <txtannotationfld.hxx>
#include <rootfrm.hxx>
#include <SwRewriter.hxx>
#include <tools/color.hxx>
#include <unotools/datetime.hxx>

#include <swmodule.hxx>
#include <strings.hrc>
#include <cmdid.h>

#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/request.hxx>
#include <sfx2/event.hxx>
#include <svl/srchitem.hxx>

#include <svl/languageoptions.hxx>
#include <svl/hint.hxx>

#include <svx/svdview.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/outliner.hxx>
#include <editeng/outlobj.hxx>

#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
#include <officecfg/Office/Writer.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>

#include <annotsh.hxx>
#include <swabstdlg.hxx>
#include <pagefrm.hxx>
#include <officecfg/Office/Common.hxx>

#include <memory>

// 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

using namespace sw::sidebarwindows;
using namespace sw::annotation;

namespace {

    enum class CommentNotificationType { Add, Remove, Modify, Resolve, RedlinedDeletion };

    bool comp_pos(const std::unique_ptr<SwAnnotationItem>& a, const std::unique_ptr<SwAnnotationItem>& b)
    {
        // sort by anchor position
        SwPosition aPosAnchorA = a->GetAnchorPosition();
        SwPosition aPosAnchorB = b->GetAnchorPosition();

        bool aAnchorAInFooter = false;
        bool aAnchorBInFooter = false;

        // 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 )
            return false;
        // if aAnchorA is not placed in a footnote, and aAnchorB is
        // force a change over
        else if( !aAnchorAInFooter && aAnchorBInFooter )
            return true;
        // 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;

        boost::property_tree::ptree aAnnotation;
        aAnnotation.put("action", (nType == CommentNotificationType::Add ? "Add" :
                                   (nType == CommentNotificationType::Remove ? "Remove" :
                                    (nType == CommentNotificationType::Modify ? "Modify" :
                                     (nType == CommentNotificationType::RedlinedDeletion ? "RedlinedDeletion" :
                                      (nType == CommentNotificationType::Resolve ? "Resolve" : "???"))))));

        aAnnotation.put("id", nPostItId);
        if (nType != CommentNotificationType::Remove && pItem != nullptr)
        {
            sw::annotation::SwAnnotationWin* pWin = pItem->mpPostIt.get();

            const SwPostItField* pField = pWin->GetPostItField();
            const SwRect& aRect = pWin->GetAnchorRect();
            tools::Rectangle aSVRect(aRect.Pos().getX(),
                                    aRect.Pos().getY(),
                                    aRect.Pos().getX() + aRect.SSize().Width(),
                                    aRect.Pos().getY() + aRect.SSize().Height());

            if (!pItem->maLayoutInfo.mPositionFromCommentAnchor)
            {
                // Comments on frames: anchor position is the corner position, not the whole frame.
                aSVRect.SetSize(Size(0, 0));
            }

            std::vector<OString> aRects;
            for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges())
            {
                const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight());
                aRects.push_back(rect.SVRect().toString());
            }
            const OString sRects = comphelper::string::join("; ", aRects);

            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()));
        }

        boost::property_tree::ptree aTree;
        aTree.add_child("comment", aAnnotation);
        std::stringstream aStream;
        boost::property_tree::write_json(aStream, aTree);
        std::string aPayload = aStream.str();

        if (pView)
        {
            pView->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, OString(aPayload));
        }
    }

    class FilterFunctor
    {
    public:
        virtual bool operator()(const SwFormatField* pField) const = 0;
        virtual ~FilterFunctor() {}
    };

    class IsPostitField : public FilterFunctor
    {
    public:
        bool operator()(const SwFormatField* pField) const override
        {
            return pField->GetField()->GetTyp()->Which() == SwFieldIds::Postit;
        }
    };

    class IsPostitFieldWithAuthorOf : public FilterFunctor
    {
        OUString m_sAuthor;
    public:
        explicit IsPostitFieldWithAuthorOf(OUString aAuthor)
            : m_sAuthor(std::move(aAuthor))
        {
        }
        bool operator()(const SwFormatField* pField) const override
        {
            if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
                return false;
            return static_cast<const SwPostItField*>(pField->GetField())->GetPar1() == m_sAuthor;
        }
    };

    class IsPostitFieldWithPostitId : public FilterFunctor
    {
        sal_uInt32 m_nPostItId;
    public:
        explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId)
            : m_nPostItId(nPostItId)
            {}

        bool operator()(const SwFormatField* pField) const override
        {
            if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
                return false;
            return static_cast<const SwPostItField*>(pField->GetField())->GetPostItId() == m_nPostItId;
        }
    };

    class IsFieldNotDeleted : public FilterFunctor
    {
    private:
        IDocumentRedlineAccess const& m_rIDRA;
        FilterFunctor const& m_rNext;

    public:
        IsFieldNotDeleted(IDocumentRedlineAccess const& rIDRA,
                const FilterFunctor & rNext)
            : m_rIDRA(rIDRA)
            , m_rNext(rNext)
        {
        }
        bool operator()(const SwFormatField* pField) const override
        {
            if (!m_rNext(pField))
                return false;
            if (!pField->GetTextField())
                return false;
            return !sw::IsFieldDeletedInModel(m_rIDRA, *pField->GetTextField());
        }
    };

    //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;

        virtual void Notify(SfxBroadcaster&, const SfxHint& rHint) override
        {
            if ( rHint.GetId() != SfxHintId::SwFormatField )
                return;
            const SwFormatFieldHint* pHint = static_cast<const SwFormatFieldHint*>(&rHint);

            bool bAllInvalidated = false;
            if (pHint->Which() == SwFormatFieldHintWhich::REMOVED)
            {
                const SwFormatField* pField = pHint->GetField();
                bAllInvalidated = pField == nullptr;
                if (!bAllInvalidated && m_rFilter(pField))
                {
                    EndListening(const_cast<SwFormatField&>(*pField));
                    std::erase(m_aFormatFields, pField);
                }
            }
            else if (pHint->Which() == SwFormatFieldHintWhich::INSERTED)
            {
                const SwFormatField* pField = pHint->GetField();
                bAllInvalidated = pField == nullptr;
                if (!bAllInvalidated && m_rFilter(pField))
                {
                    StartListening(const_cast<SwFormatField&>(*pField));
                    m_aFormatFields.push_back(pField);
                }
            }

            if (bAllInvalidated)
                FillVector();

            return;
        }

    public:
        FieldDocWatchingStack(std::vector<std::unique_ptr<SwAnnotationItem>>& in, SwDocShell &rDocShell, FilterFunctor& rFilter)
            : m_aSidebarItems(in)
            , m_rDocShell(rDocShell)
            , m_rFilter(rFilter)
        {
            FillVector();
            StartListening(m_rDocShell);
        }
        void FillVector()
        {
            EndListeningToAllFields();
            m_aFormatFields.clear();
            m_aFormatFields.reserve(m_aSidebarItems.size());
            for (auto const& p : m_aSidebarItems)
            {
                const SwFormatField& rField = p->GetFormatField();
                if (!m_rFilter(&rField))
                    continue;
                StartListening(const_cast<SwFormatField&>(rField));
                m_aFormatFields.push_back(&rField);
            }
        }
        void EndListeningToAllFields()
        {
            for (auto const& pField : m_aFormatFields)
            {
                EndListening(const_cast<SwFormatField&>(*pField));
            }
        }
        virtual ~FieldDocWatchingStack() override
        {
            EndListeningToAllFields();
            EndListening(m_rDocShell);
        }
        const SwFormatField* pop()
        {
            if (m_aFormatFields.empty())
                return nullptr;
            const SwFormatField* p = m_aFormatFields.back();
            EndListening(const_cast<SwFormatField&>(*p));
            m_aFormatFields.pop_back();
            return p;
        }
    };

// a RAII object that sets Ignore redline flag, and restores previous redline flags in dtor
class CommentDeleteFlagsRestoreImpl : public SwPostItMgr::CommentDeleteFlagsRestore
{
public:
    CommentDeleteFlagsRestoreImpl(SwWrtShell* shell)
        : m_pWrtShell(shell)
        , m_eRestreFlags(m_pWrtShell->GetRedlineFlags())
    {
        m_pWrtShell->SetRedlineFlags(m_eRestreFlags | RedlineFlags::Ignore);
    }
    ~CommentDeleteFlagsRestoreImpl() { m_pWrtShell->SetRedlineFlags(m_eRestreFlags); }

private:
    SwWrtShell* m_pWrtShell;
    RedlineFlags m_eRestreFlags;
};

bool isOwnFileFormat(SfxMedium* pMedium)
{
    // Assume that unsaved documents are own format
    return !pMedium || !pMedium->GetFilter() || pMedium->GetFilter()->IsOwnFormat();
}

// anonymous namespace

SwPostItMgr::SwPostItMgr(SwView* pView)
    : mpView(pView)
    , mpWrtShell(mpView->GetDocShell()->GetWrtShell())
    , mpEditWin(&mpView->GetEditWin())
    , mnEventId(nullptr)
    , mbWaitingForCalcRects(false)
    , mpActivePostIt(nullptr)
    , mbLayout(false)
    , mbLayoutHeight(0)
    , mbLayouting(false)
    , mbReadOnly(mpView->GetDocShell()->IsReadOnly())
    , mbDeleteNote(true)
{
    if(!mpView->GetDrawView() )
        mpView->GetWrtShell().MakeDrawView();

    //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()));

            if((*it)->mpPostIt && (*it)->mpPostIt->GetPostItField())
                lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, (*it)->mpPostIt->GetPostItField()->GetPostItId());

            std::unique_ptr<SwAnnotationItem> p = std::move(*it);
            it = mvPostItFields.erase(it);
            if (GetActiveSidebarWin() == p->mpPostIt)
                SetActiveSidebarWin(nullptr);
            p->mpPostIt.disposeAndClear();

            if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
            {
                const SwPostItField* pPostItField = static_cast<const SwPostItField*>(p->GetFormatField().GetField());
                lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, pPostItField->GetPostItId());
            }

            bRemoved = true;
        }
        else
            ++it;
    }

    if ( !bRemoved )
        return false;

    // 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();
    }

    return true;
}

SwAnnotationItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus)
{
    if (bCheckExistence)
    {
        for (auto const& 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_castconst 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) );
            }
        }
    }
    else if ( 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, truefalse );

                    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)
                    {
                        const bool 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 (auto const& 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;
            }
        }
    }
    else if ( rHint.GetId() == SfxHintId::StyleSheetModifiedExtended )
    {
        const SfxStyleSheetModifiedHint * pStyleHint = static_cast<const SfxStyleSheetModifiedHint*>(&rHint);
        for (const auto& 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;
            }
            defaultbreak;
        }
    }
}

void SwPostItMgr::Focus(const SfxBroadcaster& rBC)
{
    if (!mpWrtShell->GetViewOptions()->IsPostIts())
    {
        SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_NOTES);
        mpView->ExecViewOptions(aRequest);
    }

    for (auto const& 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;
    }

    bool bChange = false;
    bool bRepair = false;
    PreparePageContainer();
    if ( !mvPostItFields.empty() )
    {
        IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
        for (auto const& pItem : mvPostItFields)
        {
            if (!pItem->UseElement(*mpWrtShell->GetLayout(), rIDRA))
            {
                OSL_FAIL("PostIt is not in doc or other wrong use");
                bRepair = true;
                continue;
            }
            const SwRect aOldAnchorRect( pItem->maLayoutInfo.mPosition );
            const SwPostItHelper::SwLayoutStatus eOldLayoutStatus = pItem->mLayoutStatus;
            const SwNodeOffset nOldStartNodeIdx( pItem->maLayoutInfo.mnStartNodeIdx );
            const sal_Int32 nOldStartContent( pItem->maLayoutInfo.mnStartContent );
            {
                // update layout information
                const SwTextAnnotationField* pTextAnnotationField =
                    dynamic_castconst SwTextAnnotationField* >( pItem->GetFormatField().GetTextField() );
                const ::sw::mark::MarkBase* pAnnotationMark =
                    pTextAnnotationField != nullptr ? pTextAnnotationField->GetAnnotationMark() : nullptr;
                if ( pAnnotationMark != nullptr )
                {
                    pItem->mLayoutStatus =
                        SwPostItHelper::getLayoutInfos(
                            pItem->maLayoutInfo,
                            pItem->GetAnchorPosition(),
                            pAnnotationMark );
                }
                else
                {
                    pItem->mLayoutStatus =
                        SwPostItHelper::getLayoutInfos( pItem->maLayoutInfo, pItem->GetAnchorPosition() );
                }
            }
            bChange = bChange
                      || pItem->maLayoutInfo.mPosition != aOldAnchorRect
                      || pItem->mLayoutStatus != eOldLayoutStatus
                      || pItem->maLayoutInfo.mnStartNodeIdx != nOldStartNodeIdx
                      || pItem->maLayoutInfo.mnStartContent != nOldStartContent;
        }

        // 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 (auto const& 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;
                }
            }

            const tools::ULong aPageNum = pItem->maLayoutInfo.mnPageNumber;
            if (aPageNum > mPages.size())
            {
                const tools::ULong nNumberOfPages = mPages.size();
                mPages.reserve(aPageNum);
                for (tools::ULong j=0; j<aPageNum - nNumberOfPages; ++j)
                    mPages.emplace_back( new SwPostItPageItem());
            }
            mPages[aPageNum-1]->mvSidebarItems.push_back(pItem.get());
            mPages[aPageNum-1]->mPageRect = pItem->maLayoutInfo.mPageFrame;
            mPages[aPageNum-1]->eSidebarPosition = pItem->maLayoutInfo.meSidebarPosition;
        }

        if (!bChange && mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
        {
            tools::Long nLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
            if( nLayoutHeight > mbLayoutHeight )
            {
                if (mPages[0]->bScrollbar || HasScrollbars())
                    bChange = true;
            }
            else if( nLayoutHeight < mbLayoutHeight )
            {
                if (mPages[0]->bScrollbar || !BorderOverPageBorder(1))
                    bChange = true;
            }
        }
    }

    if ( bRepair )
        CheckForRemovedPostIts();

    mbLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
    mbWaitingForCalcRects = false;
    return bChange;
}

bool SwPostItMgr::HasScrollbars() const
{
    for (auto const& postItField : mvPostItFields)
    {
        if (postItField->mbShow && postItField->mpPostIt && postItField->mpPostIt->HasScrollbar())
            return true;
    }
    return false;
}

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());
    }
    else if (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 (auto const& page : mPages)
    {
        page->mvSidebarItems.clear();
        if (mvPostItFields.empty())
            page->bScrollbar = false;
    }
}

VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationItem&&nbsp;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();
        }

        rCreated = true;
    }
    return rItem.mpPostIt;
}

void SwPostItMgr::LayoutPostIts()
{
    const bool bLoKitActive = comphelper::LibreOfficeKit::isActive();
    const bool bTiledAnnotations = comphelper::LibreOfficeKit::isTiledAnnotations();
    const bool bShowNotes = ShowNotes();

    const bool bEnableMapMode = bLoKitActive && !mpEditWin->IsMapModeEnabled();
    if (bEnableMapMode)
        mpEditWin->EnableMapMode();

    std::set<VclPtr<SwAnnotationWin>> aCreatedPostIts;
    if ( !mvPostItFields.empty() && !mbWaitingForCalcRects )
    {
        mbLayouting = true;

        //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 (auto const& 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);
                        }

                        pPostIt->SetChangeTracking(
                            pItem->mLayoutStatus,
                            GetColorAnchor(pItem->maLayoutInfo.mRedlineAuthor));
                        pPostIt->SetSidebarPosition(pPage->eSidebarPosition);

                        if (pPostIt->GetPostItField()->GetParentPostItId() != 0)
                            pPostIt->SetFollow(true);

                        tools::Long aPostItHeight = 0;
                        if (bShowNotes)
                        {
                            tools::Long mlPageBorder = 0;
                            tools::Long mlPageEnd = 0;

                            if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
                            {
                                // x value for notes positioning
                                mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true);
                                //bending point
                                mlPageEnd =
                                    mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
                                    ? pItem->maLayoutInfo.mPagePrtArea.Left()
                                    : pPage->mPageRect.Left() + 350;
                            }
                            else if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
                            {
                                // x value for notes positioning
                                mlPageBorder = mpEditWin->LogicToPixel( Point(pPage->mPageRect.Right(), 0)).X() + GetSidebarBorderWidth(true);
                                //bending point
                                mlPageEnd =
                                    mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
                                    ? pItem->maLayoutInfo.mPagePrtArea.Right() :
                                    pPage->mPageRect.Right() - 350;
                            }

                            tools::Long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y();

                            aPostItHeight = ( pPostIt->GetPostItTextHeight() < pPostIt->GetMinimumSizeWithoutMeta()
                                              ? pPostIt->GetMinimumSizeWithoutMeta()
                                              : pPostIt->GetPostItTextHeight() )
                                            + pPostIt->GetMetaHeight();
                            pPostIt->SetPosSizePixelRect( mlPageBorder ,
                                                          Y - GetInitialAnchorDistance(),
                                                          GetSidebarWidth(true),
                                                          aPostItHeight,
                                                          mlPageEnd );
                        }

                        pPostIt->SetAnchorRect(pItem->maLayoutInfo.mPosition);

                        pPostIt->ChangeSidebarItem( *pItem );

                        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;
                    }
                    else if (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 (auto const& 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()));
                                else if ( 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()));
                                else if ( 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 (auto const& visiblePostIt : aVisiblePostItList)
                    {
                        visiblePostIt->SetPosAndSize();
                    }

                    bool bOldScrollbar = pPage->bScrollbar;
                    pPage->bScrollbar = false;
                    bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
                }

                for (auto const& visiblePostIt : aVisiblePostItList)
                {
                    if (bLoKitActive && !bTiledAnnotations)
                    {
                        if (visiblePostIt->GetSidebarItem().mbPendingLayout && visiblePostIt->GetSidebarItem().mLayoutStatus != SwPostItHelper::DELETED)
                        {
                            // Notify about a just inserted comment.
                            aCreatedPostIts.insert(visiblePostIt);
                        }
                        else if (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 (auto const& 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 (const auto& 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!");
        return false;
    }

    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
        return false;
}

void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage)
{
    assert(nPage < mPages.size());
    if (nPage >= mPages.size())
        return;
    for (auto const& 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;

        bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
        mpEditWin->EnableMapMode();
        rRenderContext.Push(vcl::PushFlags::MAPMODE);
        Point aOffset(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
        MapMode aMapMode(rRenderContext.GetMapMode());
        aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset);
        rRenderContext.SetMapMode(aMapMode);
        Size aSize(rRenderContext.PixelToLogic(pPostIt->GetSizePixel()));
        tools::Rectangle aRectangle(Point(0, 0), aSize);

        pPostIt->PaintTile(rRenderContext, aRectangle);

        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;

    const bool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage);
    const bool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage);
    const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
    for (auto const& 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);

        if (item->mbShow)
        {
            bool bBottom  = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y()+pPostIt->VirtualSize().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
            bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() >=   (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
            if ( bBottom && bTop)
            {
                    pPostIt->ShowNote();
            }
            else
            {
                if ( mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() < (mPages[aPage-1]->mPageRect.Top()+aSidebarheight))
                {
                    if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Top()));
                    else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Top()));
                }
                else
                {
                    if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Bottom()));
                    else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Bottom()));
                }
            }
        }
    }
    mPages[aPage-1]->lOffset += lScroll;
    if ( (bOldUp != ArrowEnabled(KEY_PAGEUP,aPage)) ||(bOldDown != ArrowEnabled(KEY_PAGEDOWN,aPage)) )
    {
        mpEditWin->Invalidate(GetBottomScrollRect(aPage));
        mpEditWin->Invalidate(GetTopScrollRect(aPage));
    }
}

void SwPostItMgr::AutoScroll(const SwAnnotationWin* pPostIt,const tools::ULong aPage )
{
    // otherwise all notes are visible
    if (!mPages[aPage-1]->bScrollbar)
        return;

    const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
    const bool bBottom  = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
    const bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
    if ( !(bBottom && bTop))
    {
        const tools::Long aDiff = bBottom ? mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Top() + aSidebarheight)).Y() - pPostIt->GetPosPixel().Y() :
                                        mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Bottom() - aSidebarheight)).Y() - (pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height());
        // this just adds the missing value to get the next a* GetScrollSize() after aDiff
        // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100
        const auto nScrollSize = GetScrollSize();
        assert(nScrollSize);
        const tools::Long lScroll = bBottom ? (aDiff + ( nScrollSize - (aDiff % nScrollSize))) : (aDiff - (nScrollSize + (aDiff % nScrollSize)));
        Scroll(lScroll, aPage);
    }
}

void SwPostItMgr::MakeVisible(const SwAnnotationWin* pPostIt )
{
    tools::Long aPage = -1;
    // we don't know the page yet, let's find it ourselves
    std::vector<SwPostItPageItem*>::size_type n=0;
    for (auto const& page : mPages)
    {
        for (auto const& item : page->mvSidebarItems)
        {
            if (item->mpPostIt==pPostIt)
            {
                aPage = n+1;
                break;
            }
        }
        ++n;
    }
    if (aPage!=-1)
        AutoScroll(pPostIt,aPage);
    tools::Rectangle aNoteRect (Point(pPostIt->GetPosPixel().X(),pPostIt->GetPosPixel().Y()-5),pPostIt->GetSizePixel());
    if (!aNoteRect.IsEmpty())
        mpWrtShell->MakeVisible(SwRect(mpEditWin->PixelToLogic(aNoteRect)));
}

bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection,tools::ULong aPage) const
{
    switch (aDirection)
    {
        case KEY_PAGEUP:
            {
                return (mPages[aPage-1]->lOffset != 0);
            }
        case KEY_PAGEDOWN:
            {
                return (!BorderOverPageBorder(aPage));
            }
        defaultreturn false;
    }
}

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

    //rBorder is the page rect
    const tools::Rectangle aBorder         = mpEditWin->LogicToPixel(rBorder);
    tools::Long            lTopBorder      = aBorder.Top() + 5;
    tools::Long            lBottomBorder   = aBorder.Bottom() - 5;
    const tools::Long      lVisibleHeight  = lBottomBorder - lTopBorder; //aBorder.GetHeight() ;
    const size_t    nPostItListSize = aVisiblePostItList.size();
    tools::Long            lTranslatePos   = 0;
    bool            bScrollbars     = false;

    // 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 (auto const& visiblePostIt : aVisiblePostItList)
                visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta()));
        }
        else
        {
            for (auto const& 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;
 }

std::vector<SwFormatField*> SwPostItMgr::UpdatePostItsParentInfo()
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=92 G=92

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.