[Libreoffice-commits] core.git: sw/inc sw/Library_sw.mk sw/source

Jim Raykowski (via logerrit) logerrit at kemper.freedesktop.org
Thu Jul 30 10:04:55 UTC 2020


 sw/Library_sw.mk                                       |    1 
 sw/inc/ndtxt.hxx                                       |    8 
 sw/inc/strings.hrc                                     |    2 
 sw/inc/swtypes.hxx                                     |    3 
 sw/source/core/crsr/crstrvl.cxx                        |    8 
 sw/source/core/txtnode/ndtxt.cxx                       |   20 +
 sw/source/uibase/docvw/FrameControlsManager.cxx        |  108 +++++++
 sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx |  237 +++++++++++++++++
 sw/source/uibase/docvw/edtwin.cxx                      |  133 +++++++++
 sw/source/uibase/docvw/edtwin2.cxx                     |    4 
 sw/source/uibase/inc/FrameControlsManager.hxx          |    5 
 sw/source/uibase/inc/OutlineContentVisibilityWin.hxx   |   49 +++
 sw/source/uibase/inc/edtwin.hxx                        |    7 
 sw/source/uibase/inc/wrtsh.hxx                         |    4 
 sw/source/uibase/utlui/content.cxx                     |   61 ++++
 sw/source/uibase/wrtsh/wrtsh1.cxx                      |  120 ++++++++
 16 files changed, 765 insertions(+), 5 deletions(-)

New commits:
commit 835cd06a047717dfe5e0f117959f3c042e13b21b
Author:     Jim Raykowski <raykowj at gmail.com>
AuthorDate: Mon Jul 27 17:52:41 2020 -0800
Commit:     Mike Kaganski <mike.kaganski at collabora.com>
CommitDate: Thu Jul 30 12:03:56 2020 +0200

    tdf#38093 Writer outline folding - outline visibility and on canvas ui
    
    Patch 2/6
    
    Outline content visibility and on canvas collapse/expand control button
    implementations.
    
    Change-Id: I8481125b102d2f07bfcfce91e1379d8e786a7aa2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/99653
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>

diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index 5f7c204f0e6a..2de7d6b0e4dc 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -622,6 +622,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
     sw/source/uibase/docvw/SidebarTxtControlAcc \
     sw/source/uibase/docvw/SidebarWinAcc \
     sw/source/uibase/docvw/HeaderFooterWin \
+    sw/source/uibase/docvw/OutlineContentVisibilityWin \
     sw/source/uibase/docvw/edtdd \
     sw/source/uibase/docvw/edtwin \
     sw/source/uibase/docvw/edtwin2 \
diff --git a/sw/inc/ndtxt.hxx b/sw/inc/ndtxt.hxx
index 9edf16dab96e..9dd3a5abe78a 100644
--- a/sw/inc/ndtxt.hxx
+++ b/sw/inc/ndtxt.hxx
@@ -596,6 +596,14 @@ public:
      */
     void SetAttrOutlineLevel(int nLevel);
 
+    /**
+     * @brief GetAttrOutlineContentVisible
+     * @param bOutlineContentVisibleAttr    the value stored in RES_PARATR_GRABBAG for 'OutlineContentVisibleAttr'
+     * @return true if 'OutlineContentVisibleAttr' is found in RES_PARATR_GRABBAG
+     */
+    bool GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr);
+    void SetAttrOutlineContentVisible(bool bVisible);
+
     bool IsEmptyListStyleDueToSetOutlineLevelAttr() const { return mbEmptyListStyleSetDueToSetOutlineLevelAttr;}
     void SetEmptyListStyleDueToSetOutlineLevelAttr();
     void ResetEmptyListStyleDueToResetOutlineLevelAttr();
diff --git a/sw/inc/strings.hrc b/sw/inc/strings.hrc
index 46a4ec21f64a..86733bf6bb3a 100644
--- a/sw/inc/strings.hrc
+++ b/sw/inc/strings.hrc
@@ -643,6 +643,8 @@
 #define STR_OUTLINE_TRACKING_DEFAULT            NC_("STR_OUTLINE_TRACKING_DEFAULT", "Default")
 #define STR_OUTLINE_TRACKING_FOCUS              NC_("STR_OUTLINE_TRACKING_FOCUS", "Focus")
 #define STR_OUTLINE_TRACKING_OFF                NC_("STR_OUTLINE_TRACKING_OFF", "Off")
+#define STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY       NC_("STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY", "Toggle Outline Content Visibility")
+#define STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT   NC_("STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT", "hold Ctrl or right-click to include sub levels")
 
 #define STR_EXPANDALL                           NC_("STR_EXPANDALL", "Expand All")
 #define STR_COLLAPSEALL                         NC_("STR_COLLAPSEALL", "Collapse All")
diff --git a/sw/inc/swtypes.hxx b/sw/inc/swtypes.hxx
index 119ef21d7997..0f7d3af3e962 100644
--- a/sw/inc/swtypes.hxx
+++ b/sw/inc/swtypes.hxx
@@ -242,7 +242,8 @@ enum class FrameControlType
     PageBreak,
     Header,
     Footer,
-    FloatingTable
+    FloatingTable,
+    Outline
 };
 
 #endif
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index 5ee44842b56f..5bea3f176289 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -1278,11 +1278,13 @@ bool SwCursorShell::GetContentAtPos( const Point& rPt,
             && IsAttrAtPos::Outline & rContentAtPos.eContentAtPos
             && !rNds.GetOutLineNds().empty() )
         {
-            const SwTextNode* pONd = pTextNd->FindOutlineNodeOfLevel(MAXLEVEL-1, GetLayout());
-            if( pONd )
+            // only for nodes in outline nodes
+            SwOutlineNodes::size_type nPos;
+            if(rNds.GetOutLineNds().Seek_Entry(pTextNd, &nPos))
             {
                 rContentAtPos.eContentAtPos = IsAttrAtPos::Outline;
-                rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pONd, true, false, ExpandMode::ExpandFootnote);
+                rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pTextNd, true, false, ExpandMode::ExpandFootnote);
+                rContentAtPos.aFnd.pNode = pTextNd;
                 bRet = true;
             }
         }
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 6200eb040abd..56fa589eaef5 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -71,6 +71,7 @@
 #include <numrule.hxx>
 #include <docsh.hxx>
 #include <SwNodeNum.hxx>
+#include <svl/grabbagitem.hxx>
 #include <svl/intitem.hxx>
 #include <list.hxx>
 #include <sortedobjs.hxx>
@@ -4036,6 +4037,25 @@ void SwTextNode::SetAttrOutlineLevel(int nLevel)
     }
 }
 
+bool SwTextNode::GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr)
+{
+    SfxGrabBagItem aGrabBagItem(dynamic_cast<const SfxGrabBagItem&>(GetAttr(RES_PARATR_GRABBAG)));
+    auto it = aGrabBagItem.GetGrabBag().find("OutlineContentVisibleAttr");
+    if (it != aGrabBagItem.GetGrabBag().end())
+    {
+        it->second >>= bOutlineContentVisibleAttr;
+        return true;
+    }
+    return false;
+}
+
+void SwTextNode::SetAttrOutlineContentVisible(bool bVisible)
+{
+    SfxGrabBagItem aGrabBagItem(RES_PARATR_GRABBAG);
+    aGrabBagItem.GetGrabBag()["OutlineContentVisibleAttr"] <<= bVisible;
+    GetTextNode()->SetAttr(aGrabBagItem);
+}
+
 // #i70748#
 
 void SwTextNode::SetEmptyListStyleDueToSetOutlineLevelAttr()
diff --git a/sw/source/uibase/docvw/FrameControlsManager.cxx b/sw/source/uibase/docvw/FrameControlsManager.cxx
index 9d68bc950435..63bca779d473 100644
--- a/sw/source/uibase/docvw/FrameControlsManager.cxx
+++ b/sw/source/uibase/docvw/FrameControlsManager.cxx
@@ -17,6 +17,10 @@
 #include <viewopt.hxx>
 #include <view.hxx>
 #include <wrtsh.hxx>
+#include <txtfrm.hxx>
+#include <OutlineContentVisibilityWin.hxx>
+#include <ndtxt.hxx>
+#include <IDocumentOutlineNodes.hxx>
 
 using namespace std;
 
@@ -33,6 +37,7 @@ SwFrameControlsManager::~SwFrameControlsManager()
 void SwFrameControlsManager::dispose()
 {
     m_aControls.clear();
+    m_aTextNodeContentFrameMap.clear();
 }
 
 SwFrameControlPtr SwFrameControlsManager::GetControl( FrameControlType eType, const SwFrame* pFrame )
@@ -179,6 +184,109 @@ SwFrameMenuButtonBase::SwFrameMenuButtonBase( SwEditWin* pEditWin, const SwFrame
 {
 }
 
+void SwFrameControlsManager::SetOutlineContentVisibilityButtons()
+{
+    // remove entries with outline node keys that are not in the outline nodes list
+    IDocumentOutlineNodes::tSortedOutlineNodeList aOutlineNodes;
+    m_pEditWin->GetView().GetWrtShell().getIDocumentOutlineNodesAccess()->getOutlineNodes(aOutlineNodes);
+    std::map<const SwTextNode*, const SwContentFrame*>::iterator it = m_aTextNodeContentFrameMap.begin();
+    while(it != m_aTextNodeContentFrameMap.end())
+    {
+        const SwNode* pNd = it->first;
+        IDocumentOutlineNodes::tSortedOutlineNodeList::iterator i = std::find(aOutlineNodes.begin(), aOutlineNodes.end(), pNd);
+        if (i == aOutlineNodes.end())
+        {
+            RemoveControlsByType(FrameControlType::Outline, it->second);
+            it = m_aTextNodeContentFrameMap.erase(it);
+        }
+        else
+            it++;
+    }
+    for (SwNode* pNd : m_pEditWin->GetView().GetWrtShell().GetNodes().GetOutLineNds())
+    {
+        bool bOutlineContentVisibleAttr = true;
+        pNd->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+        if (!bOutlineContentVisibleAttr)
+            SetOutlineContentVisibilityButton(pNd->GetTextNode());
+    }
+}
+
+void SwFrameControlsManager::SetOutlineContentVisibilityButton(const SwTextNode* pTextNd)
+{
+    const SwContentFrame* pContentFrame = pTextNd->getLayoutFrame(nullptr);
+
+    // has node frame changed or been deleted
+    std::map<const SwTextNode*, const SwContentFrame*>::iterator iter = m_aTextNodeContentFrameMap.find(pTextNd);
+    if (iter != m_aTextNodeContentFrameMap.end())
+    {
+        const SwContentFrame* pFrameWas = iter->second;
+        if (pContentFrame != pFrameWas)
+        {
+            // frame does not match frame in map for node
+            RemoveControlsByType(FrameControlType::Outline, pFrameWas);
+            m_aTextNodeContentFrameMap.erase(iter);
+        }
+    }
+    if (pContentFrame && !pContentFrame->IsInDtor())
+    {
+        // frame is not being destroyed and isn't in map
+        m_aTextNodeContentFrameMap.insert(make_pair(pTextNd, pContentFrame));
+    }
+    else
+    {
+        if (pContentFrame)
+        {
+            // frame is being destroyed
+            RemoveControlsByType(FrameControlType::Outline, pContentFrame);
+        }
+        return;
+    }
+
+    // Check if we already have the control
+    SwFrameControlPtr pControl;
+
+    SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::Outline];
+
+    SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pContentFrame);
+    if (lb != rControls.end() && !(rControls.key_comp()(pContentFrame, lb->first)))
+    {
+        pControl = lb->second;
+    }
+    else
+    {
+        SwFrameControlPtr pNewControl =
+                std::make_shared<SwFrameControl>(VclPtr<SwOutlineContentVisibilityWin>::Create(
+                                        m_pEditWin, pContentFrame).get());
+        rControls.insert(lb, make_pair(pContentFrame, pNewControl));
+        pControl.swap(pNewControl);
+    }
+
+    SwOutlineContentVisibilityWin* pWin = dynamic_cast<SwOutlineContentVisibilityWin *>(pControl->GetWindow());
+    assert(pWin != nullptr) ;
+    pWin->Set();
+
+    if (pWin->GetSymbol() == SymbolType::ARROW_RIGHT)
+    {
+        // show expand button immediatly
+        pWin->Show();
+        // outline content might not be folded, this happens on undo, outline moves, and folded outline content reveals
+        SwOutlineNodes::size_type nPos;
+        SwOutlineNodes rOutlineNds = m_pEditWin->GetView().GetWrtShell().GetNodes().GetOutLineNds();
+        if (rOutlineNds.Seek_Entry(const_cast<SwTextNode*>(pTextNd), &nPos))
+        {
+            // don't toggle if next node is an outline node or end node
+            SwNodeIndex aIdx(*pTextNd, 1);
+            if (!(aIdx.GetNode().IsEndNode() || ((nPos + 1 < rOutlineNds.size()) && &aIdx.GetNode() == rOutlineNds[nPos +1]))
+                    && aIdx.GetNode().IsContentNode() && aIdx.GetNode().GetContentNode()->getLayoutFrame(nullptr))
+            {
+                m_pEditWin->GetView().GetWrtShell().ToggleOutlineContentVisibility(nPos, true); // force fold
+            }
+        }
+    }
+    else if (!pWin->IsVisible() && pWin->GetSymbol() == SymbolType::ARROW_DOWN)
+        pWin->ShowAll(true);
+}
+
 const SwPageFrame* SwFrameMenuButtonBase::GetPageFrame() const
 {
     if (m_pFrame->IsPageFrame())
diff --git a/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx b/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx
new file mode 100644
index 000000000000..44772f367813
--- /dev/null
+++ b/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx
@@ -0,0 +1,237 @@
+/* -*- 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/.
+ */
+
+#include <edtwin.hxx>
+#include <OutlineContentVisibilityWin.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <wrtsh.hxx>
+
+#include <memory>
+
+#include <IDocumentOutlineNodes.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <vcl/button.hxx>
+#include <vcl/event.hxx>
+#include <strings.hrc>
+#include <svx/svdview.hxx>
+
+#define BUTTON_WIDTH 18
+#define BUTTON_HEIGHT 20
+
+SwOutlineContentVisibilityWin::SwOutlineContentVisibilityWin(SwEditWin* pEditWin,
+                                                             const SwFrame* pFrame)
+    : PushButton(pEditWin, 0)
+    , m_pEditWin(pEditWin)
+    , m_pFrame(pFrame)
+    , m_nDelayAppearing(0)
+    , m_bDestroyed(false)
+{
+    SetSizePixel(Size(BUTTON_WIDTH, BUTTON_HEIGHT));
+
+    m_aDelayTimer.SetTimeout(50);
+    m_aDelayTimer.SetInvokeHandler(LINK(this, SwOutlineContentVisibilityWin, DelayHandler));
+}
+
+void SwOutlineContentVisibilityWin::dispose()
+{
+    m_bDestroyed = true;
+    m_aDelayTimer.Stop();
+
+    m_pEditWin.clear();
+    m_pFrame = nullptr;
+
+    PushButton::dispose();
+}
+
+void SwOutlineContentVisibilityWin::Set()
+{
+    const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(GetFrame());
+
+    // outline node frame containing folded outline node content might be folded so need to hide it
+    if (!pTextFrame || pTextFrame->IsInDtor())
+    {
+        SetSymbol(SymbolType::DONTKNOW);
+        Hide();
+        return;
+    }
+    const SwTextNode* pTextNode = pTextFrame->GetTextNodeFirst();
+    SwWrtShell& rSh = GetEditWin()->GetView().GetWrtShell();
+    const SwOutlineNodes& rOutlineNodes = rSh.GetNodes().GetOutLineNds();
+    rOutlineNodes.Seek_Entry(static_cast<SwNode*>(const_cast<SwTextNode*>(pTextNode)),
+                             &m_nOutlinePos);
+    assert(m_nOutlinePos != SwOutlineNodes::npos);
+
+    // don't set if no content and no subs with content
+    auto nPos = m_nOutlinePos;
+    SwNode* pSttNd = rOutlineNodes[nPos];
+    SwNode* pEndNd;
+    SwNodeIndex aIdx(*pSttNd);
+    while (true)
+    {
+        if (rOutlineNodes.size() > ++nPos)
+            pEndNd = rOutlineNodes[nPos];
+        else
+            pEndNd = &rSh.GetNodes().GetEndOfContent();
+        if (!pSttNd->IsEndNode())
+            aIdx.Assign(*pSttNd, +1);
+        if (pSttNd->IsEndNode()
+            || ((&aIdx.GetNode() == pEndNd && pEndNd->IsEndNode())
+                || (&aIdx.GetNode() == pEndNd && pSttNd->IsTextNode() && pEndNd->IsTextNode()
+                    && pSttNd->GetTextNode()->GetAttrOutlineLevel()
+                           >= pEndNd->GetTextNode()->GetAttrOutlineLevel())))
+        {
+            SetSymbol(SymbolType::DONTKNOW);
+            Hide();
+            return;
+        }
+        if (&aIdx.GetNode() != pEndNd)
+            break;
+        pSttNd = pEndNd;
+    }
+
+    // set symbol displayed on button
+    SetSymbol(rSh.IsOutlineContentFolded(m_nOutlinePos) ? SymbolType::ARROW_RIGHT
+                                                        : SymbolType::ARROW_DOWN);
+
+    // set quick help
+    SwOutlineNodes::size_type nOutlineNodesCount
+        = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+    int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(m_nOutlinePos);
+    OUString sQuickHelp(SwResId(STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY));
+    if (m_nOutlinePos + 1 < nOutlineNodesCount
+        && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(m_nOutlinePos + 1) > nLevel)
+        sQuickHelp += " (" + SwResId(STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT) + ")";
+    SetQuickHelpText(sQuickHelp);
+
+    // Set the position of the window
+    SwRect aSwRect = GetFrame()->getFrameArea(); // not far in margin
+    //SwRect aSwRect = GetFrame()->GetPaintArea(); // far in margin
+    aSwRect.AddTop(GetFrame()->GetTopMargin());
+    Point aPxPt(GetEditWin()->GetOutDev()->LogicToPixel(
+        aSwRect.TopLeft() - (aSwRect.TopLeft() - aSwRect.BottomLeft()) / 2));
+    aPxPt.AdjustX(-GetSizePixel().getWidth() + 1);
+    aPxPt.AdjustY(-GetSizePixel().getHeight() / 2);
+    SetPosPixel(aPxPt);
+}
+
+void SwOutlineContentVisibilityWin::ShowAll(bool bShow)
+{
+    if (bShow)
+    {
+        m_nDelayAppearing = 0;
+        if (!m_bDestroyed && m_aDelayTimer.IsActive())
+            m_aDelayTimer.Stop();
+        if (!m_bDestroyed)
+            m_aDelayTimer.Start();
+    }
+    else
+        Hide();
+}
+
+bool SwOutlineContentVisibilityWin::Contains(const Point& rDocPt) const
+{
+    ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel());
+    if (aRect.IsInside(rDocPt))
+        return true;
+    return false;
+}
+
+void SwOutlineContentVisibilityWin::ToggleOutlineContentVisibility(const bool bSubs)
+{
+    SwWrtShell& rSh = GetEditWin()->GetView().GetWrtShell();
+    rSh.LockView(true);
+    if (GetEditWin()->GetView().GetDrawView()->IsTextEdit())
+        rSh.EndTextEdit();
+    if (GetEditWin()->GetView().IsDrawMode())
+        GetEditWin()->GetView().LeaveDrawCreate();
+    rSh.EnterStdMode();
+    if (bSubs)
+    {
+        // toggle including sub levels
+        SwOutlineNodes::size_type nPos = m_nOutlinePos;
+        SwOutlineNodes::size_type nOutlineNodesCount
+            = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+        int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(m_nOutlinePos);
+        bool bFold = rSh.IsOutlineContentFolded(m_nOutlinePos);
+        do
+        {
+            if (rSh.IsOutlineContentFolded(nPos) == bFold)
+                rSh.ToggleOutlineContentVisibility(nPos);
+        } while (++nPos < nOutlineNodesCount
+                 && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel);
+    }
+    else
+        rSh.ToggleOutlineContentVisibility(m_nOutlinePos);
+    SetSymbol(rSh.IsOutlineContentFolded(m_nOutlinePos) ? SymbolType::ARROW_RIGHT
+                                                        : SymbolType::ARROW_DOWN);
+    rSh.GotoOutline(m_nOutlinePos); // sets cursor position
+    rSh.LockView(false);
+}
+
+void SwOutlineContentVisibilityWin::KeyInput(const KeyEvent& rKEvt)
+{
+    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+    if (!aKeyCode.GetModifier()
+        && (aKeyCode.GetCode() == KEY_RETURN || aKeyCode.GetCode() == KEY_SPACE))
+    {
+        ToggleOutlineContentVisibility(aKeyCode.GetCode() == KEY_RETURN);
+    }
+    else if (aKeyCode.GetCode() == KEY_ESCAPE)
+    {
+        Hide();
+        GrabFocusToDocument();
+    }
+}
+
+void SwOutlineContentVisibilityWin::MouseMove(const MouseEvent& rMEvt)
+{
+    if (rMEvt.IsLeaveWindow())
+    {
+        // MouseMove event may not be seen by edit window
+        // hide collapse button and grab focus to document
+        if (GetSymbol() != SymbolType::ARROW_RIGHT)
+            Hide();
+        GrabFocusToDocument();
+    }
+    else if (rMEvt.IsEnterWindow())
+    {
+        if (!m_bDestroyed && m_aDelayTimer.IsActive())
+            m_aDelayTimer.Stop();
+        // bring button to top and grab focus
+        SetZOrder(this, ZOrderFlags::First);
+        GrabFocus();
+    }
+    GetEditWin()->SetSavedOutlineFrame(const_cast<SwFrame*>(GetFrame()));
+}
+
+void SwOutlineContentVisibilityWin::MouseButtonDown(const MouseEvent& rMEvt)
+{
+    ToggleOutlineContentVisibility(rMEvt.IsRight() || rMEvt.IsMod1());
+}
+
+IMPL_LINK_NOARG(SwOutlineContentVisibilityWin, DelayHandler, Timer*, void)
+{
+    const int TICKS_BEFORE_WE_APPEAR = 5;
+    if (m_nDelayAppearing < TICKS_BEFORE_WE_APPEAR)
+    {
+        ++m_nDelayAppearing;
+        m_aDelayTimer.Start();
+        return;
+    }
+    if (GetEditWin()->GetSavedOutlineFrame() == GetFrame())
+    {
+        Show();
+        GrabFocus();
+    }
+    m_aDelayTimer.Stop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx
index 2a2d6634a0ea..8f0751024e2d 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -139,6 +139,11 @@
 #include <sfx2/event.hxx>
 #include <memory>
 
+#include <IDocumentOutlineNodes.hxx>
+#include <ndtxt.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+
 using namespace sw::mark;
 using namespace ::com::sun::star;
 
@@ -1330,6 +1335,32 @@ void SwEditWin::KeyInput(const KeyEvent &rKEvt)
         }
     }
 
+    if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        // not allowed if outline content is folded
+        sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+        if ((rSh.IsSttPara() && (nKey == KEY_BACKSPACE || nKey == KEY_LEFT))
+                || (rSh.IsEndOfPara() && (nKey == KEY_DELETE || nKey == KEY_RETURN || nKey == KEY_RIGHT)))
+        {
+            SwContentNode* pContentNode = rSh.GetCurrentShellCursor().GetContentNode();
+            SwOutlineNodes::size_type nPos;
+            if (rSh.GetDoc()->GetNodes().GetOutLineNds().Seek_Entry(pContentNode, &nPos))
+            {
+                bool bOutlineContentVisibleAttr = true;
+                pContentNode->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+                if (!bOutlineContentVisibleAttr)
+                    return; // outline node is folded
+                if (rSh.IsSttPara() && (nKey == KEY_BACKSPACE || nKey == KEY_LEFT) && (nPos-1 != SwOutlineNodes::npos))
+                {
+                    bOutlineContentVisibleAttr = true;
+                    rSh.GetDoc()->GetNodes().GetOutLineNds()[nPos-1]->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+                    if (!bOutlineContentVisibleAttr)
+                        return; // previous outline node is folded
+                }
+            }
+        }
+    }
+
     if( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE &&
         m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard )
     {
@@ -2628,6 +2659,10 @@ KEYINPUT_CHECKTABLE_INSDEL:
         }
     }
 
+    // x11 backend doesn't like not having this
+    if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+        GetFrameControlsManager().SetOutlineContentVisibilityButtons();
+
     // update the page number in the statusbar
     sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
     if( KEY_UP == nKey || KEY_DOWN == nKey || KEY_PAGEUP == nKey || KEY_PAGEDOWN == nKey )
@@ -3813,6 +3848,52 @@ void SwEditWin::MouseMove(const MouseEvent& _rMEvt)
     if( rSh.ActionPend() )
         return ;
 
+    if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        // add/remove outline collapse button
+        SwContentAtPos aSwContentAtPos(IsAttrAtPos::Outline);
+        if (rSh.GetContentAtPos(PixelToLogic(rMEvt.GetPosPixel()), aSwContentAtPos))
+        {
+            if(aSwContentAtPos.aFnd.pNode && aSwContentAtPos.aFnd.pNode->IsTextNode())
+            {
+                const SwNodes& rNds = rSh.GetDoc()->GetNodes();
+                SwOutlineNodes::size_type nPos;
+                SwContentFrame* pContentFrame = aSwContentAtPos.aFnd.pNode->GetTextNode()->getLayoutFrame(nullptr);
+                if (pContentFrame != m_pSavedOutlineFrame)
+                {
+                    // remove collapse button when saved frame is not frame at mouse position
+                    if (m_pSavedOutlineFrame && /* is it possible that m_pSavedOutlineFrame is removed? */ !m_pSavedOutlineFrame->IsInDtor() &&
+                            rNds.GetOutLineNds().Seek_Entry(static_cast<SwTextFrame*>(m_pSavedOutlineFrame)->GetTextNodeFirst(), &nPos) &&
+                            !rSh.IsOutlineContentFolded(nPos))
+                    {
+                        GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame);
+                    }
+                    m_pSavedOutlineFrame = pContentFrame;
+                }
+                // show collapse button
+                if (rNds.GetOutLineNds().Seek_Entry(aSwContentAtPos.aFnd.pNode->GetTextNode(), &nPos) &&
+                        !rSh.IsOutlineContentFolded(nPos))
+                {
+                    GetFrameControlsManager().SetOutlineContentVisibilityButton(aSwContentAtPos.aFnd.pNode->GetTextNode());
+                }
+            }
+        }
+        else if (m_pSavedOutlineFrame && !m_pSavedOutlineFrame->IsInDtor())
+        {
+            // current pointer pos is not over an outline frame
+            // previous frame was an outline frame
+            // remove collapse button if showing
+            const SwNodes& rNds = rSh.GetDoc()->GetNodes();
+            SwOutlineNodes::size_type nPos;
+            if (rNds.GetOutLineNds().Seek_Entry(static_cast<SwTextFrame*>(m_pSavedOutlineFrame)->GetTextNodeFirst(), &nPos) &&
+                    !rSh.IsOutlineContentFolded(nPos))
+            {
+                GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame);
+            }
+            m_pSavedOutlineFrame = nullptr;
+        }
+    }
+
     if( m_pShadCursor && 0 != (rMEvt.GetModifier() + rMEvt.GetButtons() ) )
     {
         m_pShadCursor.reset();
@@ -3847,6 +3928,21 @@ void SwEditWin::MouseMove(const MouseEvent& _rMEvt)
     const Point aOldPt( rSh.VisArea().Pos() );
     const bool bInsWin = rSh.VisArea().IsInside( aDocPt ) || comphelper::LibreOfficeKit::isActive();
 
+    if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        if (m_pSavedOutlineFrame && !bInsWin)
+        {
+            // the mouse pointer has left the building
+            // 86 the collapse button if showing
+            const SwNodes& rNds = rSh.GetDoc()->GetNodes();
+            SwOutlineNodes::size_type nPos;
+            rNds.GetOutLineNds().Seek_Entry(static_cast<SwTextFrame*>(m_pSavedOutlineFrame)->GetTextNodeFirst(), &nPos);
+            if (!rSh.IsOutlineContentFolded(nPos))
+                GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame);
+            m_pSavedOutlineFrame = nullptr;
+        }
+    }
+
     if( m_pShadCursor && !bInsWin )
     {
         m_pShadCursor.reset();
@@ -5275,6 +5371,11 @@ void SwEditWin::Command( const CommandEvent& rCEvt )
     case CommandEventId::Wheel:
     case CommandEventId::StartAutoScroll:
     case CommandEventId::AutoScroll:
+            if (m_pSavedOutlineFrame && rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+            {
+                GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame);
+                m_pSavedOutlineFrame = nullptr;
+            }
             m_pShadCursor.reset();
             bCallBase = !m_rView.HandleWheelCommands( rCEvt );
             break;
@@ -6421,6 +6522,38 @@ void SwEditWin::SetGraphicTwipPosition(bool bStart, const Point& rPosition)
     }
 }
 
+void SwEditWin::SetOutlineContentVisiblityButtons()
+{
+    SwWrtShell& rSh = m_rView.GetWrtShell();
+    const SwOutlineNodes& rOutlineNodes = rSh.GetDoc()->GetNodes().GetOutLineNds();
+    if (!rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNodes.size(); ++nPos)
+        {
+            bool bOutlineContentVisibleAttr = true;
+            rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+            if (!bOutlineContentVisibleAttr)
+            {
+                // unfold and then set outline content visible attr to false for persistence
+                rSh.ToggleOutlineContentVisibility(nPos);
+                rOutlineNodes[nPos]->GetTextNode()->SetAttrOutlineContentVisible(false);
+            }
+        }
+        GetFrameControlsManager().HideControls(FrameControlType::Outline);
+    }
+    else
+    {
+        for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNodes.size(); ++nPos)
+        {
+            bool bOutlineContentVisibleAttr = true;
+            rOutlineNodes[nPos]->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+            if (!bOutlineContentVisibleAttr)
+                rSh.ToggleOutlineContentVisibility(nPos, true);
+        }
+    }
+    GetView().Invalidate(); // set state of dependent slots (FN_TOGGLE_OUTLINE_CONTENT_VISIBILITY)
+}
+
 SwFrameControlsManager& SwEditWin::GetFrameControlsManager()
 {
     return *m_pFrameControlsManager;
diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx
index da67d799008f..064ddee8de01 100644
--- a/sw/source/uibase/docvw/edtwin2.cxx
+++ b/sw/source/uibase/docvw/edtwin2.cxx
@@ -49,6 +49,7 @@
 #include <IDocumentMarkAccess.hxx>
 #include <txtfrm.hxx>
 #include <ndtxt.hxx>
+#include <FrameControlsManager.hxx>
 
 static OUString lcl_GetRedlineHelp( const SwRangeRedline& rRedl, bool bBalloon )
 {
@@ -429,6 +430,9 @@ void SwEditWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle
 
     if( bPaintShadowCursor )
         m_pShadCursor->Paint();
+
+    if (pWrtShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+        GetFrameControlsManager().SetOutlineContentVisibilityButtons();
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/inc/FrameControlsManager.hxx b/sw/source/uibase/inc/FrameControlsManager.hxx
index 66685bd8b174..cb51be62f860 100644
--- a/sw/source/uibase/inc/FrameControlsManager.hxx
+++ b/sw/source/uibase/inc/FrameControlsManager.hxx
@@ -18,6 +18,8 @@
 
 class SwPageFrame;
 class SwEditWin;
+class SwContentFrame;
+class SwTextNode;
 
 typedef std::shared_ptr< SwFrameControl > SwFrameControlPtr;
 
@@ -30,6 +32,7 @@ class SwFrameControlsManager
     private:
         VclPtr<SwEditWin> m_pEditWin;
         std::map< FrameControlType, SwFrameControlPtrMap > m_aControls;
+        std::map<const SwTextNode*, const SwContentFrame*> m_aTextNodeContentFrameMap;
 
     public:
         SwFrameControlsManager( SwEditWin* pEditWin );
@@ -46,6 +49,8 @@ class SwFrameControlsManager
         void SetHeaderFooterControl( const SwPageFrame* pPageFrame, FrameControlType eType, Point aOffset );
         void SetPageBreakControl( const SwPageFrame* pPageFrame );
         void SetUnfloatTableButton( const SwFlyFrame* pFlyFrame, bool bShow, Point aTopRightPixel = Point() );
+        void SetOutlineContentVisibilityButton(const SwTextNode* pTextNd);
+        void SetOutlineContentVisibilityButtons();
 };
 
 #endif
diff --git a/sw/source/uibase/inc/OutlineContentVisibilityWin.hxx b/sw/source/uibase/inc/OutlineContentVisibilityWin.hxx
new file mode 100644
index 000000000000..13f29fd0f2eb
--- /dev/null
+++ b/sw/source/uibase/inc/OutlineContentVisibilityWin.hxx
@@ -0,0 +1,49 @@
+/* -*- 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/.
+ */
+#ifndef INCLUDED_SW_SOURCE_UIBASE_INC_OUTLINECONTENTVISIBILITYWIN_HXX
+#define INCLUDED_SW_SOURCE_UIBASE_INC_OUTLINECONTENTVISIBILITYWIN_HXX
+
+#include "edtwin.hxx"
+#include "FrameControl.hxx"
+
+class SwOutlineContentVisibilityWin : public PushButton, public ISwFrameControl
+{
+private:
+    VclPtr<SwEditWin> m_pEditWin;
+    const SwFrame* m_pFrame;
+    int m_nDelayAppearing; ///< Before we show the control, wait a few timer ticks to avoid appearing with every mouse over.
+    Timer m_aDelayTimer;
+    bool m_bDestroyed;
+    size_t m_nOutlinePos;
+
+    void ToggleOutlineContentVisibility(const bool bSubs);
+
+public:
+    SwOutlineContentVisibilityWin(SwEditWin* pEditWin, const SwFrame* pFrame);
+    virtual ~SwOutlineContentVisibilityWin() override { disposeOnce(); }
+    virtual void dispose() override;
+
+    virtual void KeyInput(const KeyEvent& rKEvt) override;
+    virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
+    virtual void MouseMove(const MouseEvent& rMEvt) override;
+    virtual void ShowAll(bool bShow) override;
+    virtual bool Contains(const Point& rDocPt) const override;
+    virtual void SetReadonly(bool /*bReadonly*/) override {}
+    virtual const SwFrame* GetFrame() override { return m_pFrame; }
+    virtual SwEditWin* GetEditWin() override { return m_pEditWin; }
+
+    void Set();
+
+private:
+    DECL_LINK(DelayHandler, Timer*, void);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/inc/edtwin.hxx b/sw/source/uibase/inc/edtwin.hxx
index 855a2ff4158c..49f09827dd9c 100644
--- a/sw/source/uibase/inc/edtwin.hxx
+++ b/sw/source/uibase/inc/edtwin.hxx
@@ -127,6 +127,8 @@ class SW_DLLPUBLIC SwEditWin final : public vcl::Window,
 
     std::unique_ptr<SwFrameControlsManager> m_pFrameControlsManager;
 
+     SwFrame* m_pSavedOutlineFrame = nullptr;
+
     void            LeaveArea(const Point &);
     void            JustifyAreaTimer();
     inline void     EnterArea();
@@ -288,7 +290,10 @@ public:
     /// Allows starting or ending a graphic move or resize action.
     void SetGraphicTwipPosition(bool bStart, const Point& rPosition);
 
-    void SetOutlineContentVisiblityButtons() {(void)this;}
+    const SwFrame* GetSavedOutlineFrame() { return m_pSavedOutlineFrame; }
+    void SetSavedOutlineFrame(SwFrame* pFrame) { m_pSavedOutlineFrame = pFrame; }
+
+    void SetOutlineContentVisiblityButtons();
 
     virtual FactoryFunction GetUITestFactory() const override;
 };
diff --git a/sw/source/uibase/inc/wrtsh.hxx b/sw/source/uibase/inc/wrtsh.hxx
index 4c02e5e465b5..8cb74403c795 100644
--- a/sw/source/uibase/inc/wrtsh.hxx
+++ b/sw/source/uibase/inc/wrtsh.hxx
@@ -489,6 +489,10 @@ typedef bool (SwWrtShell:: *FNSimpleMove)();
     /// Inserts a new annotation/comment at the current cursor position / selection.
     void InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq);
 
+    bool IsOutlineContentFolded(const size_t nPos);
+    void ToggleOutlineContentVisibility(SwNode* pNd, bool bForceFold = false);
+    void ToggleOutlineContentVisibility(const size_t nPos, bool bForceFold = false);
+
 private:
 
     SAL_DLLPRIVATE void  OpenMark();
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx
index e9d498d2b426..1fcd1873d8f3 100644
--- a/sw/source/uibase/utlui/content.cxx
+++ b/sw/source/uibase/utlui/content.cxx
@@ -89,6 +89,8 @@
 #include <fmtcntnt.hxx>
 #include <docstat.hxx>
 
+#include <viewopt.hxx>
+
 #define CTYPE_CNT   0
 #define CTYPE_CTT   1
 
@@ -2639,6 +2641,7 @@ void SwContentTree::ExecCommand(const OString& rCmd, bool bOutlineWithChildren)
 
     SwOutlineNodes::difference_type nDirLast = bUp ? -1 : 1;
     bool bStartedAction = false;
+    std::vector<SwNode*> aFoldedOutlineNdsArray;
     for (auto const& pCurrentEntry : selected)
     {
         assert(pCurrentEntry && lcl_IsContent(*pCurrentEntry, *m_xTreeView));
@@ -2660,6 +2663,28 @@ void SwContentTree::ExecCommand(const OString& rCmd, bool bOutlineWithChildren)
         if (!bStartedAction)
         {
             pShell->StartAllAction();
+            if (bUpDown)
+            {
+                if (pShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+                {
+                    // unfold all folded outline content
+                    SwOutlineNodes rOutlineNds = pShell->GetDoc()->GetNodes().GetOutLineNds();
+                    for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNds.size(); ++nPos)
+                    {
+                        SwNode* pNd = rOutlineNds[nPos];
+                        if (pNd->IsTextNode()) // should aways be true
+                        {
+                            bool bOutlineContentVisibleAttr = true;
+                            pNd->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+                            if (!bOutlineContentVisibleAttr)
+                            {
+                                aFoldedOutlineNdsArray.push_back(pNd);
+                                pShell->ToggleOutlineContentVisibility(nPos);
+                            }
+                        }
+                    }
+                }
+            }
             pShell->StartUndo(bLeftRight ? SwUndoId::OUTLINE_LR : SwUndoId::OUTLINE_UD);
             bStartedAction = true;
         }
@@ -2831,6 +2856,15 @@ void SwContentTree::ExecCommand(const OString& rCmd, bool bOutlineWithChildren)
     if (bStartedAction)
     {
         pShell->EndUndo();
+        if (bUpDown)
+        {
+            if (pShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+            {
+                // fold all outlines that were folded before chapter promote/demote
+                for (SwNode* pNd : aFoldedOutlineNdsArray)
+                    pShell->ToggleOutlineContentVisibility(pNd, true);
+            }
+        }
         pShell->EndAllAction();
         if (m_aActiveContentArr[ContentTypeId::OUTLINE])
             m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
@@ -3006,6 +3040,27 @@ void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos)
 {
     SwWrtShell *const pShell = GetWrtShell();
     pShell->StartAllAction();
+    std::vector<SwNode*> aFoldedOutlineNdsArray;
+
+    if (pShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        // unfold all folded outline content
+        SwOutlineNodes rOutlineNds = pShell->GetDoc()->GetNodes().GetOutLineNds();
+        for (SwOutlineNodes::size_type nPos = 0; nPos < rOutlineNds.size(); ++nPos)
+        {
+            SwNode* pNd = rOutlineNds[nPos];
+            if (pNd->IsTextNode()) // should aways be true
+            {
+                bool bOutlineContentVisibleAttr = true;
+                pNd->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr);
+                if (!bOutlineContentVisibleAttr)
+                {
+                    aFoldedOutlineNdsArray.push_back(pNd);
+                    pShell->ToggleOutlineContentVisibility(nPos);
+                }
+            }
+        }
+    }
     pShell->StartUndo(SwUndoId::OUTLINE_UD);
 
     SwOutlineNodes::size_type nPrevSourcePos = SwOutlineNodes::npos;
@@ -3056,6 +3111,12 @@ void SwContentTree::MoveOutline(SwOutlineNodes::size_type nTargetPos)
     }
 
     pShell->EndUndo();
+    if (pShell->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+    {
+        // fold all outlines that were folded before chapter promote/demote
+        for (SwNode* pNd : aFoldedOutlineNdsArray)
+            pShell->ToggleOutlineContentVisibility(pNd, true);
+    }
     pShell->EndAllAction();
     m_aActiveContentArr[ContentTypeId::OUTLINE]->Invalidate();
     Display(true);
diff --git a/sw/source/uibase/wrtsh/wrtsh1.cxx b/sw/source/uibase/wrtsh/wrtsh1.cxx
index 444db135021d..2c208eb2d592 100644
--- a/sw/source/uibase/wrtsh/wrtsh1.cxx
+++ b/sw/source/uibase/wrtsh/wrtsh1.cxx
@@ -100,6 +100,9 @@
 #include <comphelper/lok.hxx>
 #include <memory>
 
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+
 using namespace sw::mark;
 using namespace com::sun::star;
 namespace {
@@ -1989,5 +1992,122 @@ void SwWrtShell::InsertPostIt(SwFieldMgr& rFieldMgr, const SfxRequest& rReq)
             pFormat->Broadcast( SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::FOCUS, &GetView() ) );
     }
 }
+bool SwWrtShell::IsOutlineContentFolded(const size_t nPos)
+{
+    const SwNodes& rNodes = GetDoc()->GetNodes();
+    const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
+
+    assert(nPos < rOutlineNodes.size());
+
+    SwNode* pOutlineNode = rOutlineNodes[nPos];
+    if (pOutlineNode->IsEndNode())
+        return false;
+
+    bool bOutlineContentVisibleAttr = false;
+    if (pOutlineNode->GetTextNode()->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr))
+        return !bOutlineContentVisibleAttr;
+
+    return false;
+}
+
+void SwWrtShell::ToggleOutlineContentVisibility(SwNode* pNd, bool bForceFold)
+{
+    SwOutlineNodes::size_type nPos;
+    if (GetNodes().GetOutLineNds().Seek_Entry(pNd, &nPos))
+        ToggleOutlineContentVisibility(nPos, bForceFold);
+}
+
+void SwWrtShell::ToggleOutlineContentVisibility(size_t nPos, bool bForceFold)
+{
+    const SwNodes& rNodes = GetNodes();
+    const SwOutlineNodes& rOutlineNodes = rNodes.GetOutLineNds();
+
+    assert(nPos < rOutlineNodes.size());
+
+    SwNode* pSttNd = rOutlineNodes[nPos];
+    if (pSttNd->IsEndNode())
+        return;
+
+    SwNode* pEndNd = &rNodes.GetEndOfContent();
+    if (rOutlineNodes.size() > nPos + 1)
+        pEndNd = rOutlineNodes[nPos + 1];
+
+    if (pSttNd->GetTableBox() || pSttNd->GetIndex() < rNodes.GetEndOfExtras().GetIndex())
+    {
+        // limit folding to within table box
+        if (pSttNd->EndOfSectionIndex() < pEndNd->GetIndex() )
+            pEndNd = pSttNd->EndOfSectionNode();
+    }
+    // if pSttNd isn't in table but pEndNd is, skip over all outline nodes in table
+    else if (pEndNd->GetTableBox())
+    {
+        pEndNd = &rNodes.GetEndOfContent();
+        for (size_t nOutlinePos = nPos + 2; nOutlinePos < rOutlineNodes.size(); nOutlinePos++)
+        {
+            if (!(rOutlineNodes[nOutlinePos]->GetTableBox()))
+            {
+                pEndNd = rOutlineNodes[nOutlinePos];
+                break;
+            }
+        }
+    }
+
+    if (IsOutlineContentFolded(nPos) && !bForceFold)
+    {
+        // unfold
+        SwNodeIndex aIdx(*pSttNd, +1);
+        MakeFrames(GetDoc(), aIdx, *pEndNd);
+
+        pSttNd->GetTextNode()->SetAttrOutlineContentVisible(true);
+
+        if (GetViewOptions()->IsShowOutlineContentVisibilityButton())
+        {
+            // remove fold button if focus is not on outline frame control window
+            SwContentFrame* pFrame = pSttNd->GetTextNode()->getLayoutFrame(nullptr);
+            if (pFrame && !pFrame->IsInDtor())
+            {
+                SwFrameControlPtr pOutlineFrameControl = GetView().GetEditWin().GetFrameControlsManager().GetControl(FrameControlType::Outline, pFrame);
+                if (pOutlineFrameControl && pOutlineFrameControl->GetWindow() && !pOutlineFrameControl->GetWindow()->HasFocus())
+                    GetView().GetEditWin().GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, pFrame);
+            }
+
+            // fold revealed outline nodes that have collapsed content
+            while (aIdx != *pEndNd)
+            {
+                SwNode* pTmpNd = &aIdx.GetNode();
+                if (pTmpNd->IsTextNode() && pTmpNd->GetTextNode()->IsOutline())
+                {
+                    SwTextNode* pTmpTextNd = pTmpNd->GetTextNode();
+                    bool bOutlineContentVisibleAttr = true;
+                    if (pTmpTextNd->GetAttrOutlineContentVisible(bOutlineContentVisibleAttr) &&
+                            !bOutlineContentVisibleAttr)
+                    {
+                        SwOutlineNodes::size_type iPos;
+                        if (rOutlineNodes.Seek_Entry(pTmpTextNd, &iPos))
+                        {
+                            if (pTmpTextNd->getLayoutFrame(nullptr))
+                                ToggleOutlineContentVisibility(iPos, true);
+                        }
+                    }
+                }
+                aIdx++;
+            }
+        }
+    }
+    else
+    {
+        // fold
+        for (SwNodeIndex aIdx(*pSttNd, +1); &aIdx.GetNode() != pEndNd; aIdx++)
+        {
+            SwNode* pNd = &aIdx.GetNode();
+            if (pNd->IsContentNode())
+                pNd->GetContentNode()->DelFrames(nullptr);
+            else if (pNd->IsTableNode())
+                pNd->GetTableNode()->DelFrames(nullptr);
+        }
+        pSttNd->GetTextNode()->SetAttrOutlineContentVisible(false);
+    }
+    GetView().GetEditWin().Invalidate();
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list