officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu |    
8 
 sd/Library_sd.mk                                                         |    
1 
 sd/UIConfig_simpress.mk                                                  |    
2 
 sd/inc/strings.hrc                                                       |    
1 
 sd/source/ui/app/sddll.cxx                                               |    
2 
 sd/source/ui/dlg/NotesChildWindow.cxx                                    |  
632 ++++++++++
 sd/source/ui/inc/NotesChildWindow.hxx                                    |  
108 +
 sd/source/ui/view/ViewShellBase.cxx                                      |   
24 
 sd/source/ui/view/drvwshrg.cxx                                           |    
3 
 sd/uiconfig/simpress/menubar/menubar.xml                                 |    
1 
 sd/uiconfig/simpress/ui/noteschildwindow.ui                              |   
41 
 sd/uiconfig/simpress/ui/notespanelcontextmenu.ui                         |    
9 
 12 files changed, 832 insertions(+)

New commits:
commit c4c1ca58bb587a56a30c7f4817c346054ce37f2a
Author:     Sarper Akdemir <sarper.akdemir.ext...@allotropia.de>
AuthorDate: Thu Nov 9 20:18:44 2023 +0300
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Thu Feb 22 09:47:34 2024 +0100

    tdf#33603: sd: add notes panel for normal view
    
    Change-Id: Ibc6e8a3f126c443453c5ecab52ba988a4f4f56e6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159288
    Tested-by: Jenkins
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git 
a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu 
b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
index e9355220de4a..b46b7b34fac7 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu
@@ -24,6 +24,14 @@
           <value xml:lang="en-US">S~lide</value>
         </prop>
       </node>
+      <node oor:name=".uno:NotesChildWindow" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Notes Panel</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
       <node oor:name=".uno:PageMenu" oor:op="replace">
         <prop oor:name="Label" oor:type="xs:string">
           <value xml:lang="en-US">~Page</value>
diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk
index 01c546ad3dca..bdf7b146da91 100644
--- a/sd/Library_sd.mk
+++ b/sd/Library_sd.mk
@@ -231,6 +231,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\
        sd/source/ui/dlg/AnimationChildWindow \
        sd/source/ui/dlg/LayerTabBar \
        sd/source/ui/dlg/NavigatorChildWindow \
+       sd/source/ui/dlg/NotesChildWindow \
        sd/source/ui/dlg/PaneChildWindows \
        sd/source/ui/dlg/PaneShells \
        sd/source/ui/dlg/SpellDialogChildWindow \
diff --git a/sd/UIConfig_simpress.mk b/sd/UIConfig_simpress.mk
index 9d98d611de82..ee778eaeb91a 100644
--- a/sd/UIConfig_simpress.mk
+++ b/sd/UIConfig_simpress.mk
@@ -148,6 +148,8 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/simpress,\
        sd/uiconfig/simpress/ui/notebookbar_groupedbar_full \
        sd/uiconfig/simpress/ui/notebookbar_groupedbar_compact \
        sd/uiconfig/simpress/ui/notebookbar_online \
+       sd/uiconfig/simpress/ui/noteschildwindow \
+       sd/uiconfig/simpress/ui/notespanelcontextmenu \
        sd/uiconfig/simpress/ui/optimpressgeneralpage \
        sd/uiconfig/simpress/ui/pagesfieldbox \
        sd/uiconfig/simpress/ui/photoalbum \
diff --git a/sd/inc/strings.hrc b/sd/inc/strings.hrc
index 08a5bb7cc8ce..5056a8030966 100644
--- a/sd/inc/strings.hrc
+++ b/sd/inc/strings.hrc
@@ -289,6 +289,7 @@
 #define STR_PRESOBJ_OUTLINE                             
NC_("STR_PRESOBJ_OUTLINE", "Click to add Text" )
 #define STR_PRESOBJ_TEXT                                
NC_("STR_PRESOBJ_TEXT", "Click to add Text" )
 #define STR_PRESOBJ_NOTESTEXT                           
NC_("STR_PRESOBJ_NOTESTEXT", "Click to add Notes" )
+#define STR_PRESOBJ_NOTESTEXT_MISSING                   
NC_("STR_PRESOBJ_NOTESTEXT_MISSING", "Notes placeholder object is missing for 
the current slide." )
 #define STR_PRESOBJ_TITLE_MOBILE                        
NC_("STR_PRESOBJ_TITLE_MOBILE", "Double-tap to add Title" )
 #define STR_PRESOBJ_OUTLINE_MOBILE                      
NC_("STR_PRESOBJ_OUTLINE_MOBILE", "Double-tap to add Text" )
 #define STR_PRESOBJ_TEXT_MOBILE                         
NC_("STR_PRESOBJ_TEXT_MOBILE", "Double-tap to add Text" )
diff --git a/sd/source/ui/app/sddll.cxx b/sd/source/ui/app/sddll.cxx
index aded023da375..b21ce1c549e1 100644
--- a/sd/source/ui/app/sddll.cxx
+++ b/sd/source/ui/app/sddll.cxx
@@ -48,6 +48,7 @@
 #include <OutlineViewShell.hxx>
 #include <OutlineViewShellBase.hxx>
 #include <PaneChildWindows.hxx>
+#include <NotesChildWindow.hxx>
 #include <SpellDialogChildWindow.hxx>
 #include <SlideSorterViewShell.hxx>
 #include <SlideSorterViewShellBase.hxx>
@@ -178,6 +179,7 @@ void SdDLL::RegisterControllers(SdModule* pMod)
     ::sd::LeftPaneImpressChildWindow::RegisterChildWindow(false, pMod);
     ::sd::LeftPaneDrawChildWindow::RegisterChildWindow(false, pMod);
     ::sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod);
+    ::sd::NotesChildWindow::RegisterChildWindow(false, pMod);
     DevelopmentToolChildWindow::RegisterChildWindow(false, pMod);
 
     ::sd::SdNavigatorWrapper::RegisterChildWindow(false, pMod, 
SfxChildWindowFlags::NEVERHIDE);
diff --git a/sd/source/ui/dlg/NotesChildWindow.cxx 
b/sd/source/ui/dlg/NotesChildWindow.cxx
new file mode 100644
index 000000000000..ebcc94d316e3
--- /dev/null
+++ b/sd/source/ui/dlg/NotesChildWindow.cxx
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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 <DrawDocShell.hxx>
+#include <DrawViewShell.hxx>
+#include <EventMultiplexer.hxx>
+#include <NotesChildWindow.hxx>
+#include <Outliner.hxx>
+#include <ViewShellBase.hxx>
+#include <app.hrc>
+#include <config_wasm_strip.h>
+#include <drawdoc.hxx>
+#include <drawview.hxx>
+#include <sdpage.hxx>
+#include <sdresid.hxx>
+
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/numitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <strings.hrc>
+
+namespace sd
+{
+SFX_IMPL_DOCKINGWINDOW_WITHID(NotesChildWindow, SID_NOTES_WINDOW);
+
+NotesChildWindow::NotesChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId,
+                                   SfxBindings* pBindings, SfxChildWinInfo* 
pInfo)
+    : SfxChildWindow(pParentWindow, nId)
+{
+    VclPtr<NotesChildDockingWindow> pWin
+        = VclPtr<NotesChildDockingWindow>::Create(pBindings, this, 
pParentWindow);
+    SetWindow(pWin);
+    SetAlignment(SfxChildAlignment::BOTTOM);
+    pWin->setDeferredProperties();
+    pWin->SetSizePixel({ 200, 200 });
+    pWin->set_border_width(3);
+    pWin->set_margin_top(11);
+    pWin->Initialize(pInfo);
+}
+
+NotesChildDockingWindow::NotesChildDockingWindow(SfxBindings* _pBindings,
+                                                 SfxChildWindow* pChildWindow, 
Window* pParent)
+    : SfxDockingWindow(_pBindings, pChildWindow, pParent, 
"NotesChildEditWindow",
+                       "modules/simpress/ui/noteschildwindow.ui")
+    , 
mpViewShellBase(ViewShellBase::GetViewShellBase(_pBindings->GetDispatcher()->GetFrame()))
+    , m_xEditWindow(
+          new NotesEditWindow(*this, 
m_xBuilder->weld_scrolled_window("scrolledwin", true)))
+{
+    mpOutliner = 
std::make_unique<Outliner>(&mpViewShellBase->GetDocShell()->GetPool(),
+                                            OutlinerMode::TextObject);
+
+    mpOutlinerView = std::make_unique<OutlinerView>(mpOutliner.get(), nullptr);
+    mpOutliner->InsertView(mpOutlinerView.get());
+
+    m_xEditWindowWeld
+        = std::make_unique<weld::CustomWeld>(*m_xBuilder, 
"noteschildeditview", *m_xEditWindow);
+}
+
+DrawViewShell* NotesChildDockingWindow::GetDrawViewShell()
+{
+    auto pDocShell = GetViewShellBase()->GetDocShell();
+    if (!pDocShell)
+        return nullptr;
+
+    return dynamic_cast<DrawViewShell*>(pDocShell->GetViewShell());
+}
+
+NotesChildDockingWindow::~NotesChildDockingWindow() { disposeOnce(); }
+
+void NotesChildDockingWindow::dispose()
+{
+    m_xEditWindow.reset();
+    m_xEditWindowWeld.reset();
+    SfxDockingWindow::dispose();
+}
+
+NotesEditWindow::NotesEditWindow(NotesChildDockingWindow& rParentWindow,
+                                 std::unique_ptr<weld::ScrolledWindow> 
pScrolledWindow)
+    : mrParentWindow(rParentWindow)
+    , m_xScrolledWindow(std::move(pScrolledWindow))
+    , aModifyIdle("NotesEditWindow ModifyIdle")
+{
+    aModifyIdle.SetInvokeHandler(LINK(this, NotesEditWindow, ModifyTimerHdl));
+    aModifyIdle.SetPriority(TaskPriority::LOWEST);
+
+    SetAcceptsTab(true);
+    m_xScrolledWindow->connect_vadjustment_changed(LINK(this, NotesEditWindow, 
ScrollHdl));
+}
+
+NotesEditWindow::~NotesEditWindow()
+{
+    aModifyIdle.Stop();
+    m_xScrolledWindow.reset();
+    if (!mrParentWindow.GetViewShellBase())
+        return;
+
+    
mrParentWindow.GetViewShellBase()->GetEventMultiplexer()->RemoveEventListener(
+        LINK(this, NotesEditWindow, EventMultiplexerListener));
+}
+
+IMPL_LINK(NotesEditWindow, EventMultiplexerListener, 
tools::EventMultiplexerEvent&, rEvent, void)
+{
+    switch (rEvent.meEventId)
+    {
+        case EventMultiplexerEventId::CurrentPageChanged:
+        case EventMultiplexerEventId::MainViewRemoved:
+        case EventMultiplexerEventId::MainViewAdded:
+            provideNoteText();
+            break;
+        default:
+            break;
+    }
+}
+
+void NotesEditWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+    Size aSize(pDrawingArea->get_size_request());
+    if (aSize.Width() == -1)
+        aSize.setWidth(500);
+    if (aSize.Height() == -1)
+        aSize.setHeight(100);
+    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+    SetOutputSizePixel(aSize);
+
+    weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+    EnableRTL(false);
+
+    const StyleSettings& rStyleSettings = 
Application::GetSettings().GetStyleSettings();
+    Color aBgColor = rStyleSettings.GetWindowColor();
+
+    OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+    rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
+    rDevice.SetBackground(aBgColor);
+
+    Size aOutputSize(rDevice.PixelToLogic(aSize));
+
+    EditView* pEditView = GetEditView();
+    pEditView->setEditViewCallbacks(this);
+
+    EditEngine* pEditEngine = GetEditEngine();
+    pEditEngine->SetPaperSize(aOutputSize);
+    pEditEngine->SetRefDevice(&rDevice);
+
+    pEditEngine->SetControlWord(pEditEngine->GetControlWord() | 
EEControlBits::MARKFIELDS);
+    pEditView->SetOutputArea(::tools::Rectangle(Point(0, 0), aOutputSize));
+
+    pEditView->SetBackgroundColor(aBgColor);
+
+    pDrawingArea->set_cursor(PointerStyle::Text);
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+    InitAccessible();
+#endif
+
+    mrParentWindow.GetViewShellBase()->GetEventMultiplexer()->AddEventListener(
+        LINK(this, NotesEditWindow, EventMultiplexerListener));
+
+    // For setGlobalScale to work correctly EEControlBits::STRETCHING must be 
set.
+    
mrParentWindow.GetOutliner()->SetControlWord(mrParentWindow.GetOutliner()->GetControlWord()
+                                                 | EEControlBits::STRETCHING);
+    mrParentWindow.GetOutliner()->setGlobalScale(30.0, 30.0);
+
+    provideNoteText();
+
+    GetEditEngine()->SetStatusEventHdl(LINK(this, NotesEditWindow, 
EditStatusHdl));
+}
+
+void NotesEditWindow::doScroll()
+{
+    if (m_xEditView)
+    {
+        auto currentDocPos = m_xEditView->GetVisArea().Top();
+        auto nDiff = currentDocPos - 
m_xScrolledWindow->vadjustment_get_value();
+        // we expect SetScrollBarRange callback to be triggered by Scroll
+        // to set where we ended up
+        m_xEditView->Scroll(0, nDiff);
+    }
+}
+
+void NotesEditWindow::setScrollBarRange()
+{
+    EditEngine* pEditEngine = GetEditEngine();
+    if (!pEditEngine)
+        return;
+    if (!m_xScrolledWindow)
+        return;
+    EditView* pEditView = GetEditView();
+    if (!pEditView)
+        return;
+
+    int nVUpper = pEditEngine->GetTextHeight();
+    int nVCurrentDocPos = pEditView->GetVisArea().Top();
+    const Size aOut(pEditView->GetOutputArea().GetSize());
+    int nVStepIncrement = aOut.Height() * 2 / 10;
+    int nVPageIncrement = aOut.Height() * 8 / 10;
+    int nVPageSize = aOut.Height();
+
+    /* limit the page size to below nUpper because gtk's 
gtk_scrolled_window_start_deceleration has
+       effectively...
+
+       lower = gtk_adjustment_get_lower
+       upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
+
+       and requires that upper > lower or the deceleration animation never ends
+    */
+    nVPageSize = std::min(nVPageSize, nVUpper);
+
+    m_xScrolledWindow->vadjustment_configure(nVCurrentDocPos, 0, nVUpper, 
nVStepIncrement,
+                                             nVPageIncrement, nVPageSize);
+}
+
+void NotesEditWindow::showContextMenu(const Point& rPos)
+{
+    ::tools::Rectangle aRect(rPos, Size(1, 1));
+    weld::Widget* pPopupParent = GetDrawingArea();
+    std::unique_ptr<weld::Builder> xBuilder(
+        Application::CreateBuilder(pPopupParent, 
"modules/simpress/ui/notespanelcontextmenu.ui"));
+    std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+
+    auto xFrame = 
mrParentWindow.GetDrawViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
+    OUString 
aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame));
+
+    SfxItemSet aSet(mrParentWindow.GetOutlinerView()->GetAttribs());
+    int nInsertPos = 0;
+    xMenu->insert(nInsertPos++, ".uno:Bold",
+                  vcl::CommandInfoProvider::GetMenuLabelForCommand(
+                      
vcl::CommandInfoProvider::GetCommandProperties(".uno:Bold", aModuleName)),
+                  nullptr, nullptr,
+                  vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Bold", 
xFrame),
+                  TRISTATE_TRUE);
+
+    if (aSet.GetItemState(EE_CHAR_WEIGHT) == SfxItemState::SET)
+    {
+        if (aSet.Get(EE_CHAR_WEIGHT).GetWeight() == WEIGHT_BOLD)
+            xMenu->set_active(".uno:Bold", true);
+    }
+
+    xMenu->insert(nInsertPos++, ".uno:Italic",
+                  vcl::CommandInfoProvider::GetMenuLabelForCommand(
+                      
vcl::CommandInfoProvider::GetCommandProperties(".uno:Italic", aModuleName)),
+                  nullptr, nullptr,
+                  
vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Italic", xFrame),
+                  TRISTATE_TRUE);
+
+    if (aSet.GetItemState(EE_CHAR_ITALIC) == SfxItemState::SET)
+    {
+        if (aSet.Get(EE_CHAR_ITALIC).GetPosture() != ITALIC_NONE)
+            xMenu->set_active(".uno:Italic", true);
+    }
+
+    xMenu->insert(
+        nInsertPos++, ".uno:Underline",
+        vcl::CommandInfoProvider::GetMenuLabelForCommand(
+            vcl::CommandInfoProvider::GetCommandProperties(".uno:Underline", 
aModuleName)),
+        nullptr, nullptr, 
vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Underline", xFrame),
+        TRISTATE_TRUE);
+
+    if (aSet.GetItemState(EE_CHAR_UNDERLINE) == SfxItemState::SET)
+    {
+        if (aSet.Get(EE_CHAR_UNDERLINE).GetLineStyle() != LINESTYLE_NONE)
+            xMenu->set_active(".uno:Underline", true);
+    }
+
+    xMenu->insert(
+        nInsertPos++, ".uno:Strikeout",
+        vcl::CommandInfoProvider::GetMenuLabelForCommand(
+            vcl::CommandInfoProvider::GetCommandProperties(".uno:Strikeout", 
aModuleName)),
+        nullptr, nullptr, 
vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Strikeout", xFrame),
+        TRISTATE_TRUE);
+
+    if (aSet.GetItemState(EE_CHAR_STRIKEOUT) == SfxItemState::SET)
+    {
+        if (aSet.Get(EE_CHAR_STRIKEOUT).GetStrikeout() != STRIKEOUT_NONE)
+            xMenu->set_active(".uno:Strikeout", true);
+    }
+
+    xMenu->insert_separator(nInsertPos++, "separator2");
+
+    xMenu->insert(nInsertPos++, ".uno:Copy",
+                  vcl::CommandInfoProvider::GetMenuLabelForCommand(
+                      
vcl::CommandInfoProvider::GetCommandProperties(".uno:Copy", aModuleName)),
+                  nullptr, nullptr,
+                  vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Copy", 
xFrame),
+                  TRISTATE_INDET);
+
+    xMenu->insert(nInsertPos++, ".uno:Paste",
+                  vcl::CommandInfoProvider::GetMenuLabelForCommand(
+                      
vcl::CommandInfoProvider::GetCommandProperties(".uno:Paste", aModuleName)),
+                  nullptr, nullptr,
+                  
vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Paste", xFrame),
+                  TRISTATE_INDET);
+
+    bool bCanPaste = false;
+    {
+        TransferableDataHelper aDataHelper(
+            TransferableDataHelper::CreateFromClipboard(GetClipboard()));
+        bCanPaste = aDataHelper.GetFormatCount() != 0;
+    }
+
+    xMenu->insert_separator(nInsertPos++, "separator3");
+
+    xMenu->insert(
+        nInsertPos++, ".uno:DefaultBullet",
+        vcl::CommandInfoProvider::GetMenuLabelForCommand(
+            
vcl::CommandInfoProvider::GetCommandProperties(".uno:DefaultBullet", 
aModuleName)),
+        nullptr, nullptr,
+        vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:DefaultBullet", 
xFrame),
+        TRISTATE_TRUE);
+
+    ESelection aSel(GetEditView()->GetSelection());
+    aSel.Adjust();
+    bool bBulletsEnabled = true;
+    for (sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; nPara++)
+    {
+        if (mrParentWindow.GetOutliner()->GetDepth(nPara) == -1)
+        {
+            bBulletsEnabled = false;
+            break;
+        }
+    }
+
+    if (bBulletsEnabled)
+        xMenu->set_active(".uno:DefaultBullet", true);
+
+    xMenu->insert(
+        nInsertPos++, ".uno:OutlineLeft",
+        vcl::CommandInfoProvider::GetMenuLabelForCommand(
+            vcl::CommandInfoProvider::GetCommandProperties(".uno:OutlineLeft", 
aModuleName)),
+        nullptr, nullptr,
+        vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:OutlineLeft", 
xFrame),
+        TRISTATE_INDET);
+
+    xMenu->insert(
+        nInsertPos++, ".uno:OutlineRight",
+        vcl::CommandInfoProvider::GetMenuLabelForCommand(
+            
vcl::CommandInfoProvider::GetCommandProperties(".uno:OutlineRight", 
aModuleName)),
+        nullptr, nullptr,
+        vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:OutlineRight", 
xFrame),
+        TRISTATE_INDET);
+
+    xMenu->set_sensitive(".uno:Copy", 
mrParentWindow.GetOutlinerView()->HasSelection());
+    xMenu->set_sensitive(".uno:Paste", bCanPaste);
+
+    auto sId = xMenu->popup_at_rect(pPopupParent, aRect);
+
+    if (sId == ".uno:Copy")
+    {
+        mrParentWindow.GetOutlinerView()->Copy();
+    }
+    else if (sId == ".uno:Paste")
+    {
+        mrParentWindow.GetOutlinerView()->PasteSpecial();
+    }
+    else if (sId == ".uno:DefaultBullet")
+    {
+        mrParentWindow.GetOutlinerView()->ToggleBullets();
+    }
+    else if (sId == ".uno:OutlineLeft" || sId == ".uno:OutlineRight")
+    {
+        bool isOutlineLeft = sId == ".uno:OutlineLeft";
+        for (sal_Int32 nPara = aSel.nStartPara; nPara <= aSel.nEndPara; 
nPara++)
+        {
+            auto nCurrentDepth = mrParentWindow.GetOutliner()->GetDepth(nPara);
+            if (nCurrentDepth == -1)
+                continue;
+
+            mrParentWindow.GetOutlinerView()->SetDepth(nPara, isOutlineLeft ? 
--nCurrentDepth
+                                                                            : 
++nCurrentDepth);
+        }
+    }
+    else if (!sId.isEmpty())
+    {
+        SfxItemSet aEditAttr(mrParentWindow.GetOutlinerView()->GetAttribs());
+        SfxItemSet aNewAttr(mrParentWindow.GetOutliner()->GetEmptyItemSet());
+
+        if (sId == ".uno:Bold")
+        {
+            FontWeight eFW = aEditAttr.Get(EE_CHAR_WEIGHT).GetWeight();
+            aNewAttr.Put(
+                SvxWeightItem(eFW == WEIGHT_NORMAL ? WEIGHT_BOLD : 
WEIGHT_NORMAL, EE_CHAR_WEIGHT));
+        }
+        else if (sId == ".uno:Italic")
+        {
+            FontItalic eFI = aEditAttr.Get(EE_CHAR_ITALIC).GetPosture();
+            aNewAttr.Put(
+                SvxPostureItem(eFI == ITALIC_NORMAL ? ITALIC_NONE : 
ITALIC_NORMAL, EE_CHAR_ITALIC));
+        }
+        else if (sId == ".uno:Underline")
+        {
+            FontLineStyle eFU = 
aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle();
+            aNewAttr.Put(SvxUnderlineItem(
+                eFU == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, 
EE_CHAR_UNDERLINE));
+        }
+        else if (sId == ".uno:Strikeout")
+        {
+            FontStrikeout eFSO = 
aEditAttr.Get(EE_CHAR_STRIKEOUT).GetStrikeout();
+            aNewAttr.Put(SvxCrossedOutItem(
+                eFSO == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, 
EE_CHAR_STRIKEOUT));
+        }
+
+        mrParentWindow.GetOutlinerView()->SetAttribs(aNewAttr);
+    }
+}
+
+void NotesEditWindow::EditViewScrollStateChange() { setScrollBarRange(); }
+
+EditView* NotesEditWindow::GetEditView() const
+{
+    if (!mrParentWindow.GetOutlinerView())
+        return nullptr;
+    return &mrParentWindow.GetOutlinerView()->GetEditView();
+}
+
+EditEngine* NotesEditWindow::GetEditEngine() const
+{
+    if (!mrParentWindow.GetOutlinerView())
+        return nullptr;
+    return &mrParentWindow.GetOutlinerView()->GetEditView().getEditEngine();
+}
+
+void NotesEditWindow::GetFocus()
+{
+    if (auto pDrawViewShell = mrParentWindow.GetDrawViewShell())
+    {
+        if (auto pDrawView = pDrawViewShell->GetDrawView())
+        {
+            pDrawView->EndTextEditCurrentView();
+            pDrawView->UnmarkAll();
+        }
+    }
+
+    if (mpTextObj && mpTextObj->IsEmptyPresObj())
+    {
+        // clear the "Click to add Notes" text on enter of the window.
+        mrParentWindow.GetOutliner()->SetToEmptyText();
+    }
+    WeldEditView::GetFocus();
+}
+
+bool NotesEditWindow::CanFocus() const
+{
+    auto pDocShell = mrParentWindow.GetViewShellBase()->GetDocShell();
+    if (pDocShell && pDocShell->IsReadOnly())
+        return false;
+
+    return mpTextObj;
+}
+
+void NotesEditWindow::LoseFocus()
+{
+    aModifyIdle.Stop();
+    if (mpTextObj)
+    {
+        if (GetText().getLength() == 0)
+        {
+            // if the notes are empty restore the placeholder text and state.
+            SdPage* pPage = 
dynamic_cast<SdPage*>(mpTextObj->getSdrPageFromSdrObject());
+            if (pPage)
+                pPage->RestoreDefaultText(mpTextObj);
+        }
+        else
+            setNotesToDoc();
+    }
+
+    WeldEditView::LoseFocus();
+}
+
+bool NotesEditWindow::Command(const CommandEvent& rCEvt)
+{
+    if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+    {
+        showContextMenu(rCEvt.GetMousePosPixel());
+        return true;
+    }
+
+    return WeldEditView::Command(rCEvt);
+}
+
+void NotesEditWindow::provideNoteText()
+{
+    removeListener();
+    mpTextObj = nullptr;
+    SetText(SdResId(STR_PRESOBJ_NOTESTEXT_MISSING));
+
+    const auto pDrawViewShell = mrParentWindow.GetDrawViewShell();
+    if (!pDrawViewShell)
+        return;
+
+    SdPage* pCurrentPage = pDrawViewShell->getCurrentPage();
+    if (!pCurrentPage)
+        return;
+
+    SdDrawDocument* pDoc = pDrawViewShell->GetDoc();
+    if (!pDoc)
+        return;
+
+    SdPage* pNotesPage = pDoc->GetSdPage((pCurrentPage->GetPageNum() - 1) >> 
1, PageKind::Notes);
+    if (!pNotesPage)
+        return;
+
+    SdrObject* pNotesObj = pNotesPage->GetPresObj(PresObjKind::Notes);
+    if (!pNotesObj)
+        return;
+
+    mpTextObj = dynamic_cast<SdrTextObj*>(pNotesObj);
+    addListener();
+    getNotesFromDoc();
+}
+
+void NotesEditWindow::removeListener()
+{
+    if (mpTextObj)
+        mpTextObj->RemoveListener(*this);
+}
+void NotesEditWindow::addListener()
+{
+    if (mpTextObj)
+        mpTextObj->AddListener(*this);
+}
+
+void NotesEditWindow::setListenerIgnored(bool bIgnore) { mbIgnoreNotifications 
= bIgnore; }
+bool NotesEditWindow::isListenerIgnored() { return mbIgnoreNotifications; }
+
+void NotesEditWindow::getNotesFromDoc()
+{
+    if (!mpTextObj)
+        return;
+
+    // Ignore notifications that will rebound from updating the text
+    SetModifyHdl(Link<LinkParamNone*, void>());
+    setListenerIgnored(true);
+
+    if (OutlinerParaObject* pPara = mpTextObj->GetOutlinerParaObject())
+        mrParentWindow.GetOutliner()->SetText(*pPara);
+
+    setListenerIgnored(false);
+    SetModifyHdl(LINK(this, NotesEditWindow, EditModifiedHdl));
+}
+
+void NotesEditWindow::setNotesToDoc()
+{
+    if (!mpTextObj)
+        return;
+
+    setListenerIgnored(true);
+
+    std::optional<OutlinerParaObject> pNewText = 
mrParentWindow.GetOutliner()->CreateParaObject();
+    mpTextObj->SetOutlinerParaObject(std::move(pNewText));
+    if (mpTextObj->IsEmptyPresObj())
+        mpTextObj->SetEmptyPresObj(false);
+
+    setListenerIgnored(false);
+}
+
+void NotesEditWindow::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+    if (isListenerIgnored())
+        return;
+
+    if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+    {
+        const SdrHint& rSdrHint = reinterpret_cast<const SdrHint&>(rHint);
+        switch (rSdrHint.GetKind())
+        {
+            case SdrHintKind::ObjectRemoved:
+            case SdrHintKind::ModelCleared:
+                provideNoteText();
+                break;
+            case SdrHintKind::ObjectChange:
+            case SdrHintKind::EndEdit:
+                getNotesFromDoc();
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+IMPL_LINK_NOARG(NotesEditWindow, EditStatusHdl, EditStatus&, void) { Resize(); 
}
+
+IMPL_LINK_NOARG(NotesEditWindow, EditModifiedHdl, LinkParamNone*, void)
+{
+    // EditEngine calls ModifyHdl many times in succession for some edits.
+    // (e.g. when deleting multiple lines)
+    // Debounce the rapid ModifyHdl calls using a timer.
+    aModifyIdle.Start();
+    return;
+}
+
+IMPL_LINK_NOARG(NotesEditWindow, ModifyTimerHdl, Timer*, void)
+{
+    setNotesToDoc();
+    aModifyIdle.Stop();
+}
+
+IMPL_LINK(NotesEditWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, 
void)
+{
+    if (EditView* pEditView = GetEditView())
+    {
+        pEditView->SetVisArea(::tools::Rectangle(Point(0, 
rScrolledWindow.vadjustment_get_value()),
+                                                 
pEditView->GetVisArea().GetSize()));
+        pEditView->Invalidate();
+    }
+    doScroll();
+}
+
+} // end of namespace ::sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sd/source/ui/inc/NotesChildWindow.hxx 
b/sd/source/ui/inc/NotesChildWindow.hxx
new file mode 100644
index 000000000000..5242595bcb24
--- /dev/null
+++ b/sd/source/ui/inc/NotesChildWindow.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+#pragma once
+
+#include <svx/weldeditview.hxx>
+#include <sfx2/dockwin.hxx>
+
+class Outliner;
+class OutlinerView;
+class SdrTextObj;
+
+namespace sd::tools
+{
+class EventMultiplexerEvent;
+}
+
+namespace sd
+{
+class ViewShellBase;
+class DrawViewShell;
+class NotesChildWindow;
+class NotesEditWindow;
+class NotesChildDockingWindow;
+
+class NotesEditWindow : public WeldEditView, public SfxListener
+{
+    NotesChildDockingWindow& mrParentWindow;
+    std::unique_ptr<weld::ScrolledWindow> m_xScrolledWindow;
+    Idle aModifyIdle;
+
+    SdrTextObj* mpTextObj = nullptr;
+    bool mbIgnoreNotifications = false;
+
+    void doScroll();
+    void setScrollBarRange();
+    void showContextMenu(const Point& rPos);
+
+    DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void);
+    DECL_LINK(EditStatusHdl, EditStatus&, void);
+    DECL_LINK(EditModifiedHdl, LinkParamNone*, void);
+    DECL_LINK(ModifyTimerHdl, Timer*, void);
+    DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void);
+
+    void removeListener();
+    void addListener();
+
+    void setListenerIgnored(bool bIgnore);
+    bool isListenerIgnored();
+
+    void getNotesFromDoc();
+    void setNotesToDoc();
+
+public:
+    NotesEditWindow(NotesChildDockingWindow& rParentWindow,
+                    std::unique_ptr<weld::ScrolledWindow> pScrolledWindow);
+    virtual ~NotesEditWindow() override;
+
+    void provideNoteText();
+    bool HasNotesPlaceholder() { return mpTextObj; }
+
+    virtual void EditViewScrollStateChange() override;
+    virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+    virtual EditView* GetEditView() const override;
+    virtual EditEngine* GetEditEngine() const override;
+    virtual void GetFocus() override;
+    virtual bool CanFocus() const override;
+    virtual void LoseFocus() override;
+    virtual bool Command(const CommandEvent& rCEvt) override;
+    virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+};
+
+class NotesChildDockingWindow final : public SfxDockingWindow
+{
+    ViewShellBase* mpViewShellBase;
+    std::unique_ptr<NotesEditWindow> m_xEditWindow;
+    std::unique_ptr<weld::CustomWeld> m_xEditWindowWeld;
+
+    std::unique_ptr<OutlinerView> mpOutlinerView;
+    std::unique_ptr<Outliner> mpOutliner;
+
+public:
+    NotesChildDockingWindow(SfxBindings* pBindings, SfxChildWindow* 
pChildWindow, Window* pParent);
+    virtual ~NotesChildDockingWindow() override;
+    void dispose() override;
+
+    OutlinerView* GetOutlinerView() { return mpOutlinerView.get(); }
+    ::Outliner* GetOutliner() { return mpOutliner.get(); }
+    ViewShellBase* GetViewShellBase() { return mpViewShellBase; }
+    DrawViewShell* GetDrawViewShell();
+};
+
+class NotesChildWindow final : public SfxChildWindow
+{
+public:
+    SFX_DECL_CHILDWINDOW_WITHID(NotesChildWindow);
+    NotesChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* 
pBindings,
+                     SfxChildWinInfo* pInfo);
+};
+
+} // end of namespace ::sd
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/sd/source/ui/view/ViewShellBase.cxx 
b/sd/source/ui/view/ViewShellBase.cxx
index 70691ce1133e..fd319fee5200 100644
--- a/sd/source/ui/view/ViewShellBase.cxx
+++ b/sd/source/ui/view/ViewShellBase.cxx
@@ -630,6 +630,15 @@ void ViewShellBase::Execute (SfxRequest& rRequest)
                 framework::FrameworkHelper::msSlideSorterURL);
             break;
 
+        case SID_NOTES_WINDOW:
+        {
+            SfxViewShell* pViewShell = SfxViewShell::Current();
+            SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
+            auto nID = rRequest.GetSlot();
+            rViewFrame.ToggleChildWindow(nID);
+            break;
+        }
+
         case SID_TOGGLE_TABBAR_VISIBILITY:
         {
             SdOptions* pOptions = 
SD_MOD()->GetSdOptions(GetDocument()->GetDocumentType());
@@ -1314,6 +1323,21 @@ void ViewShellBase::Implementation::GetSlotState 
(SfxItemSet& rSet)
                         bState = xConfiguration->hasResource(xResourceId);
                         break;
 
+                    case SID_NOTES_WINDOW:
+                    {
+                        bState = false;
+                        auto* pViewShell = SfxViewShell::Current();
+                        if (pViewShell)
+                        {
+                            auto& rViewFrame = pViewShell->GetViewFrame();
+                            if (rViewFrame.KnowsChildWindow(nItemId))
+                            {
+                                bState = rViewFrame.HasChildWindow(nItemId);
+                            }
+                        }
+                        break;
+                    }
+
                     case SID_DRAWINGMODE:
                     case SID_NORMAL_MULTI_PANE_GUI:
                     case SID_SLIDE_MASTER_MODE:
diff --git a/sd/source/ui/view/drvwshrg.cxx b/sd/source/ui/view/drvwshrg.cxx
index 792d5b833a54..9871db2e2de7 100644
--- a/sd/source/ui/view/drvwshrg.cxx
+++ b/sd/source/ui/view/drvwshrg.cxx
@@ -40,6 +40,7 @@
 #include <SpellDialogChildWindow.hxx>
 #include <GraphicViewShell.hxx>
 #include <AnimationChildWindow.hxx>
+#include <NotesChildWindow.hxx>
 
 using namespace sd;
 #define ShellClass_DrawViewShell
@@ -77,6 +78,7 @@ void DrawViewShell::InitInterface_Impl()
     GetStaticInterface()->RegisterChildWindow(
         sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
     
GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId());
+    
GetStaticInterface()->RegisterChildWindow(::sd::NotesChildWindow::GetChildWindowId());
 }
 
 // SdGraphicViewShell
@@ -103,6 +105,7 @@ void GraphicViewShell::InitInterface_Impl()
     GetStaticInterface()->RegisterChildWindow(
         sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
     
GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId());
+    
GetStaticInterface()->RegisterChildWindow(::sd::NotesChildWindow::GetChildWindowId());
 }
 
 } // end of namespace sd
diff --git a/sd/uiconfig/simpress/menubar/menubar.xml 
b/sd/uiconfig/simpress/menubar/menubar.xml
index d0155ba626bf..1ee7c7011cd5 100644
--- a/sd/uiconfig/simpress/menubar/menubar.xml
+++ b/sd/uiconfig/simpress/menubar/menubar.xml
@@ -125,6 +125,7 @@
       <menu:menuitem menu:id=".uno:StatusBarVisible" menu:style="text"/>
       <menu:menuitem menu:id=".uno:LeftPaneImpress" menu:style="text"/>
       <menu:menuitem menu:id=".uno:ToggleTabBarVisibility" menu:style="text"/>
+      <menu:menuitem menu:id=".uno:NotesChildWindow" menu:style="text"/>
       <menu:menuseparator/>
       <menu:menuitem menu:id=".uno:ShowRuler"/>
       <menu:menu menu:id=".uno:GridMenu">
diff --git a/sd/uiconfig/simpress/ui/noteschildwindow.ui 
b/sd/uiconfig/simpress/ui/noteschildwindow.ui
new file mode 100644
index 000000000000..de09708702f1
--- /dev/null
+++ b/sd/uiconfig/simpress/ui/noteschildwindow.ui
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.2 -->
+<interface domain="sd">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkBox" id="NotesChildEditWindow">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="vexpand">True</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolledwin">
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="hexpand">True</property>
+        <property name="border-width">0</property>
+        <property name="hscrollbar-policy">never</property>
+        <property name="vscrollbar-policy">always</property>
+        <property name="shadow-type">etched-out</property>
+        <child>
+          <object class="GtkViewport">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <child>
+              <object class="GtkDrawingArea" id="noteschildeditview">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="events">GDK_BUTTON_MOTION_MASK | 
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | 
GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/sd/uiconfig/simpress/ui/notespanelcontextmenu.ui 
b/sd/uiconfig/simpress/ui/notespanelcontextmenu.ui
new file mode 100644
index 000000000000..e6ca46e8a7cf
--- /dev/null
+++ b/sd/uiconfig/simpress/ui/notespanelcontextmenu.ui
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="sd">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkMenu" id="menu">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+  </object>
+</interface>

Reply via email to