include/vcl/menu.hxx                  |    3 +
 sfx2/source/dialog/StyleList.cxx      |   12 ++++---
 sfx2/source/inc/StyleList.hxx         |    1 
 vcl/inc/jsdialog/enabled.hxx          |    1 
 vcl/inc/jsdialog/jsdialogbuilder.hxx  |    3 +
 vcl/inc/jsdialog/jsdialogmessages.hxx |   18 +++++++++-
 vcl/inc/jsdialog/jsdialogsender.hxx   |    1 
 vcl/jsdialog/enabled.cxx              |    9 +++++
 vcl/jsdialog/executor.cxx             |   19 +++++++----
 vcl/jsdialog/jsdialogbuilder.cxx      |   13 ++++---
 vcl/jsdialog/jsdialogsender.cxx       |   58 +++++++++++++++++++++++++++++-----
 vcl/source/window/builder.cxx         |    2 +
 vcl/source/window/menu.cxx            |   21 ++++++++++++
 13 files changed, 136 insertions(+), 25 deletions(-)

New commits:
commit a5149eadb23825a9fb5beeda81836ad7940c3083
Author:     Szymon Kłos <szymon.k...@collabora.com>
AuthorDate: Wed Dec 4 10:41:22 2024 +0100
Commit:     Szymon Kłos <szymon.k...@collabora.com>
CommitDate: Wed Dec 11 21:51:54 2024 +0100

    jsdialog: send message for weld::Menu
    
    - we need to setup correct notifier
    - enable context menu in the style sidebar in Writer
    - handle menu actions
    - base of vcl::Window and PopupMenu is VclReferenceBase
    - prepare template function sendMessage to handle both
    
    Change-Id: I54c8dc468856c0f98495bdaf4ddd504c2f56562f
    Signed-off-by: Szymon Kłos <szymon.k...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178319
    Tested-by: Jenkins

diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx
index e241609fcdb6..21344525e2d0 100644
--- a/include/vcl/menu.hxx
+++ b/include/vcl/menu.hxx
@@ -32,6 +32,7 @@
 #include <vcl/vclreferencebase.hxx>
 #include <com/sun/star/uno/Reference.hxx>
 #include <o3tl/typed_flags_set.hxx>
+#include <tools/json_writer.hxx>
 #include <list>
 
 class OutputDevice;
@@ -402,6 +403,8 @@ public:
      */
     const OUString& get_id() const { return maID; }
 
+    virtual void DumpAsPropertyTree(tools::JsonWriter&) const;
+
 private:
     css::uno::Reference<css::accessibility::XAccessible> CreateAccessible();
 };
diff --git a/sfx2/source/dialog/StyleList.cxx b/sfx2/source/dialog/StyleList.cxx
index 8fa08c908715..d62c7a28e27c 100644
--- a/sfx2/source/dialog/StyleList.cxx
+++ b/sfx2/source/dialog/StyleList.cxx
@@ -198,7 +198,7 @@ void StyleList::CreateContextMenu()
         m_bBindingUpdate = false;
     }
     mxMenu.reset();
-    mxMenuBuilder = Application::CreateBuilder(nullptr, 
u"sfx/ui/stylecontextmenu.ui"_ustr);
+    mxMenuBuilder = Application::CreateBuilder(m_pContainer, 
u"sfx/ui/stylecontextmenu.ui"_ustr);
     mxMenu = mxMenuBuilder->weld_menu(u"menu"_ustr);
     mxMenu->set_sensitive(u"edit"_ustr, m_bCanEdit);
     mxMenu->set_sensitive(u"delete"_ustr, m_bCanDel);
@@ -1541,13 +1541,14 @@ IMPL_LINK_NOARG(StyleList, Clear, void*, void)
         i.reset();
 }
 
+IMPL_LINK(StyleList, OnPopupEnd, const OUString&, sCommand, void) { 
MenuSelect(sCommand); }
+
 void StyleList::ShowMenu(const CommandEvent& rCEvt)
 {
     CreateContextMenu();
     weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : 
m_xFmtLb.get();
-    OUString sCommand(
-        mxMenu->popup_at_rect(pTreeView, 
tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
-    MenuSelect(sCommand);
+    mxMenu->connect_activate(LINK(this, StyleList, OnPopupEnd));
+    mxMenu->popup_at_rect(pTreeView, 
tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)));
 }
 
 void StyleList::MenuSelect(const OUString& rIdent)
@@ -1680,6 +1681,9 @@ IMPL_LINK_NOARG(StyleList, MenuSelectAsyncHdl, void*, 
void)
         HideHdl();
     else if (sLastItemIdent == "show")
         ShowHdl();
+
+    mxMenu.reset();
+    mxMenuBuilder.reset();
 }
 
 // Double-click on a style sheet in the ListBox is applied.
diff --git a/sfx2/source/inc/StyleList.hxx b/sfx2/source/inc/StyleList.hxx
index ef2696e5fd34..bc8a90ada248 100644
--- a/sfx2/source/inc/StyleList.hxx
+++ b/sfx2/source/inc/StyleList.hxx
@@ -129,6 +129,7 @@ public:
     void FilterSelect(sal_uInt16 nActFilter, bool bsetFilter);
 
     DECL_LINK(NewMenuExecuteAction, void*, void);
+    DECL_LINK(OnPopupEnd, const OUString&, void);
 
     bool HasStylesHighlighterFeature() { return 
m_bModuleHasStylesHighlighterFeature; }
     void SetHighlightParaStyles(bool bSet) { m_bHighlightParaStyles = bSet; }
diff --git a/vcl/inc/jsdialog/enabled.hxx b/vcl/inc/jsdialog/enabled.hxx
index 6354b70a8de7..70a867f0acb3 100644
--- a/vcl/inc/jsdialog/enabled.hxx
+++ b/vcl/inc/jsdialog/enabled.hxx
@@ -15,6 +15,7 @@ namespace jsdialog
 {
 bool isBuilderEnabled(std::u16string_view rUIFile, bool bMobile);
 bool isBuilderEnabledForPopup(std::u16string_view rUIFile);
+bool isBuilderEnabledForMenu(std::u16string_view rUIFile);
 bool isBuilderEnabledForSidebar(std::u16string_view rUIFile);
 bool isInterimBuilderEnabledForNotebookbar(std::u16string_view rUIFile);
 }
diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx 
b/vcl/inc/jsdialog/jsdialogbuilder.hxx
index 34ff37ed48d6..d7fd64388b4b 100644
--- a/vcl/inc/jsdialog/jsdialogbuilder.hxx
+++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx
@@ -754,6 +754,9 @@ public:
 
 class JSMenu final : public SalInstanceMenu
 {
+    VclPtr<PopupMenu> m_pPopupMenu;
+    JSDialogSender* m_pSender;
+
 public:
     JSMenu(JSDialogSender* pSender, PopupMenu* pMenu, SalInstanceBuilder* 
pBuilder,
            bool bTakeOwnership);
diff --git a/vcl/inc/jsdialog/jsdialogmessages.hxx 
b/vcl/inc/jsdialog/jsdialogmessages.hxx
index fffd67fe8ac5..ab0d016ca1af 100644
--- a/vcl/inc/jsdialog/jsdialogmessages.hxx
+++ b/vcl/inc/jsdialog/jsdialogmessages.hxx
@@ -15,6 +15,7 @@
 #include <rtl/ustring.hxx>
 #include <vcl/idle.hxx>
 #include <vcl/jsdialog/executor.hxx>
+#include <vcl/menu.hxx>
 #include <vcl/window.hxx>
 
 namespace jsdialog
@@ -26,7 +27,8 @@ enum MessageType
     Close,
     Action,
     Popup,
-    PopupClose
+    PopupClose,
+    Menu,
 };
 }
 
@@ -36,6 +38,7 @@ class JSDialogMessageInfo
 public:
     jsdialog::MessageType m_eType;
     VclPtr<vcl::Window> m_pWindow;
+    VclPtr<PopupMenu> m_pMenu;
     std::unique_ptr<jsdialog::ActionDataMap> m_pData;
 
 private:
@@ -43,6 +46,7 @@ private:
     {
         this->m_eType = rInfo.m_eType;
         this->m_pWindow = rInfo.m_pWindow;
+        this->m_pMenu = rInfo.m_pMenu;
         if (rInfo.m_pData)
         {
             std::unique_ptr<jsdialog::ActionDataMap> pData(
@@ -60,6 +64,14 @@ public:
     {
     }
 
+    JSDialogMessageInfo(jsdialog::MessageType eType, VclPtr<PopupMenu> pMenu,
+                        std::unique_ptr<jsdialog::ActionDataMap> pData)
+        : m_eType(eType)
+        , m_pMenu(std::move(pMenu))
+        , m_pData(std::move(pData))
+    {
+    }
+
     JSDialogMessageInfo(const JSDialogMessageInfo& rInfo) { copy(rInfo); }
 
     JSDialogMessageInfo& operator=(JSDialogMessageInfo aInfo)
@@ -93,7 +105,8 @@ public:
 
     void clearQueue();
     void forceUpdate();
-    void sendMessage(jsdialog::MessageType eType, const VclPtr<vcl::Window>& 
pWindow,
+    template <class VclType>
+    void sendMessage(jsdialog::MessageType eType, const VclPtr<VclType>& 
pTarget,
                      std::unique_ptr<jsdialog::ActionDataMap> pData = nullptr);
 
 private:
@@ -106,6 +119,7 @@ private:
     OString generatePopupMessage(VclPtr<vcl::Window> pWindow, const 
rtl::OUString& sParentId,
                                  const rtl::OUString& sCloseId) const;
     OString generateClosePopupMessage(const rtl::OUString& sWindowId) const;
+    OString generateMenuMessage(const VclPtr<PopupMenu>& pMenu) const;
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/inc/jsdialog/jsdialogsender.hxx 
b/vcl/inc/jsdialog/jsdialogsender.hxx
index f6a899a23d90..85066e10f37d 100644
--- a/vcl/inc/jsdialog/jsdialogsender.hxx
+++ b/vcl/inc/jsdialog/jsdialogsender.hxx
@@ -63,6 +63,7 @@ public:
     virtual void sendAction(VclPtr<vcl::Window> pWindow,
                             std::unique_ptr<jsdialog::ActionDataMap> pData);
     virtual void sendPopup(VclPtr<vcl::Window> pWindow, OUString sParentId, 
OUString sCloseId);
+    virtual void sendMenu(const VclPtr<PopupMenu>& pMenu);
     virtual void sendClosePopup(vcl::LOKWindowId nWindowId);
     void flush() { mpIdleNotify->Invoke(); }
 
diff --git a/vcl/jsdialog/enabled.cxx b/vcl/jsdialog/enabled.cxx
index 172c4504ed92..cba8047ffd3d 100644
--- a/vcl/jsdialog/enabled.cxx
+++ b/vcl/jsdialog/enabled.cxx
@@ -338,6 +338,15 @@ bool isBuilderEnabledForPopup(std::u16string_view rUIFile)
     return false;
 }
 
+bool isBuilderEnabledForMenu(std::u16string_view rUIFile)
+{
+    if (// sfx2
+        rUIFile == u"sfx/ui/stylecontextmenu.ui")
+        return true;
+
+    return false;
+}
+
 bool isBuilderEnabledForSidebar(std::u16string_view rUIFile)
 {
     return // scalc
diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx
index 10bdad905db5..2385c19f4a4a 100644
--- a/vcl/jsdialog/executor.cxx
+++ b/vcl/jsdialog/executor.cxx
@@ -81,10 +81,10 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, StringMap
     if (pWidget == nullptr)
     {
         // weld::Menu doesn't have base of weld::Widget
-        if (sControlType == "menu")
+        if (rWidget == "__MENU__")
         {
             weld::Menu* pMenu = JSInstanceBuilder::Menus().Find(nWindowId);
-            if (pMenu && sAction == "activated")
+            if (pMenu && sAction == "select")
             {
                 LOKTrigger::trigger_activated(*pMenu, rData["data"]);
                 return true;
@@ -581,12 +581,17 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, StringMap
                     sal_Int32 nEntryAbsPos = o3tl::toInt32(rData["data"]);
 
                     std::unique_ptr<weld::TreeIter> 
itEntry(pTreeView->make_iterator());
-                    pTreeView->get_iter_abs_pos(*itEntry, nEntryAbsPos);
-
-                    tools::Rectangle aRect = pTreeView->get_row_area(*itEntry);
-                    CommandEvent aCommand(aRect.Center(), 
CommandEventId::ContextMenu);
+                    if (pTreeView->get_iter_abs_pos(*itEntry, nEntryAbsPos))
+                    {
+                        tools::Rectangle aRect = 
pTreeView->get_row_area(*itEntry);
+                        CommandEvent aCommand(aRect.Center(), 
CommandEventId::ContextMenu);
 
-                    LOKTrigger::trigger_popup_menu(*pTreeView, aCommand);
+                        LOKTrigger::trigger_popup_menu(*pTreeView, aCommand);
+                    }
+                    else
+                        SAL_WARN("vcl", "No absolute position found for " << 
nEntryAbsPos
+                                                                          << " 
in treeview");
+                    return true;
                 }
             }
         }
diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx
index 501f14cea09a..117b7b97cad6 100644
--- a/vcl/jsdialog/jsdialogbuilder.cxx
+++ b/vcl/jsdialog/jsdialogbuilder.cxx
@@ -364,7 +364,7 @@ JSInstanceBuilder::~JSInstanceBuilder()
         jsdialog::SendFullUpdate(OUString::number(m_nWindowId), 
u"__DIALOG__"_ustr);
     }
 
-    if (m_sTypeOfJSON == "popup")
+    if (m_sTypeOfJSON == "popup" || m_sTypeOfJSON == "menu")
         sendClosePopup(m_nWindowId);
 
     if (m_aWindowToRelease)
@@ -388,6 +388,7 @@ JSInstanceBuilder::~JSInstanceBuilder()
     }
 
     JSInstanceBuilder::Popups().Forget(OUString::number(m_nWindowId));
+    JSInstanceBuilder::Menus().Forget(OUString::number(m_nWindowId));
 }
 
 const OUString& JSInstanceBuilder::GetTypeOfJSON() const { return 
m_sTypeOfJSON; }
@@ -1950,19 +1951,21 @@ void JSMenuButton::set_active(bool bActive)
     }
 }
 
-JSMenu::JSMenu(JSDialogSender* /*pSender*/, PopupMenu* pPopupMenu, 
SalInstanceBuilder* /*pBuilder*/,
+JSMenu::JSMenu(JSDialogSender* pSender, PopupMenu* pPopupMenu, 
SalInstanceBuilder* /*pBuilder*/,
                bool bTakeOwnership)
     : SalInstanceMenu(pPopupMenu, bTakeOwnership)
+    , m_pPopupMenu(pPopupMenu)
+    , m_pSender(pSender)
 {
 }
 
 OUString JSMenu::popup_at_rect(weld::Widget* /*pParent*/, const 
tools::Rectangle& /*rRect*/,
                                weld::Placement /*ePlace*/)
 {
-    // TODO: send message
+    // Do not block with SalInstanceMenu::popup_at_rect(pParent, rRect, 
ePlace);
+    m_pSender->sendMenu(m_pPopupMenu);
 
-    // first only send menu and cancel menu
-    // no return SalInstanceMenu::popup_at_rect(pParent, rRect, ePlace);
+    // Don't return any action - simulate canceled menu
     return "";
 }
 
diff --git a/vcl/jsdialog/jsdialogsender.cxx b/vcl/jsdialog/jsdialogsender.cxx
index 04c0d2a1e639..d63e8e38b8ed 100644
--- a/vcl/jsdialog/jsdialogsender.cxx
+++ b/vcl/jsdialog/jsdialogsender.cxx
@@ -45,8 +45,8 @@ void JSDialogNotifyIdle::send(const OString& sMsg)
     }
 }
 
-void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType,
-                                     const VclPtr<vcl::Window>& pWindow,
+template <class VclType>
+void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType, const 
VclPtr<VclType>& pTarget,
                                      std::unique_ptr<jsdialog::ActionDataMap> 
pData)
 {
     std::scoped_lock aGuard(m_aQueueMutex);
@@ -54,10 +54,14 @@ void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType 
eType,
     // we want only the latest update of same type
     // TODO: also if we met full update - previous updates are not valid
     auto it = m_aMessageQueue.begin();
+    const VclReferenceBase* pRawTarget = 
static_cast<VclReferenceBase*>(pTarget);
 
     while (it != m_aMessageQueue.end())
     {
-        if (it->m_eType == eType && it->m_pWindow == pWindow)
+        const VclReferenceBase* pRawWindow = 
static_cast<VclReferenceBase*>(it->m_pWindow.get());
+        const VclReferenceBase* pRawMenu = it->m_pMenu.get();
+
+        if (it->m_eType == eType && (pRawWindow == pRawTarget || pRawMenu == 
pRawTarget))
         {
             // actions should be always sent, eg. rendering of custom entries 
in combobox
             if (eType == jsdialog::MessageType::Action)
@@ -71,7 +75,7 @@ void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType 
eType,
             it++;
     }
 
-    JSDialogMessageInfo aMessage(eType, pWindow, std::move(pData));
+    JSDialogMessageInfo aMessage(eType, pTarget, std::move(pData));
     m_aMessageQueue.push_back(aMessage);
 }
 
@@ -207,6 +211,30 @@ OString 
JSDialogNotifyIdle::generateClosePopupMessage(const OUString& sWindowId)
     return aJsonWriter.finishAndGetAsOString();
 }
 
+OString JSDialogNotifyIdle::generateMenuMessage(const VclPtr<PopupMenu>& 
pMenu) const
+{
+    if (!pMenu || !m_aNotifierWindow)
+        return OString();
+
+    tools::JsonWriter aJsonWriter;
+
+    {
+        auto aChildren = aJsonWriter.startArray("children");
+        {
+            auto aStruct = aJsonWriter.startStruct();
+            pMenu->DumpAsPropertyTree(aJsonWriter);
+        }
+    }
+
+    aJsonWriter.put("jsontype", "dialog");
+    aJsonWriter.put("type", "dropdown");
+    aJsonWriter.put("cancellable", true);
+    aJsonWriter.put("popupParent", m_aNotifierWindow->get_id());
+    aJsonWriter.put("id", m_aNotifierWindow->GetLOKWindowId());
+
+    return aJsonWriter.finishAndGetAsOString();
+}
+
 void JSDialogNotifyIdle::Invoke()
 {
     std::deque<JSDialogMessageInfo> aMessageQueue;
@@ -250,6 +278,12 @@ void JSDialogNotifyIdle::Invoke()
             case jsdialog::MessageType::PopupClose:
                 send(generateClosePopupMessage((*rMessage.m_pData)[WINDOW_ID 
""_ostr]));
                 break;
+
+            case jsdialog::MessageType::Menu:
+            {
+                send(generateMenuMessage(rMessage.m_pMenu));
+                break;
+            }
         }
     }
 }
@@ -275,7 +309,7 @@ void JSDialogSender::sendFullUpdate(bool bForce)
     if (bForce)
         mpIdleNotify->forceUpdate();
 
-    mpIdleNotify->sendMessage(jsdialog::MessageType::FullUpdate, nullptr);
+    mpIdleNotify->sendMessage(jsdialog::MessageType::FullUpdate, 
VclPtr<vcl::Window>(nullptr));
     mpIdleNotify->Start();
 }
 
@@ -285,7 +319,7 @@ void JSDialogSender::sendClose()
         return;
 
     mpIdleNotify->clearQueue();
-    mpIdleNotify->sendMessage(jsdialog::MessageType::Close, nullptr);
+    mpIdleNotify->sendMessage(jsdialog::MessageType::Close, 
VclPtr<vcl::Window>(nullptr));
     flush();
 }
 
@@ -330,8 +364,18 @@ void JSDialogSender::sendClosePopup(vcl::LOKWindowId 
nWindowId)
 
     std::unique_ptr<jsdialog::ActionDataMap> pData = 
std::make_unique<jsdialog::ActionDataMap>();
     (*pData)[WINDOW_ID ""_ostr] = OUString::number(nWindowId);
-    mpIdleNotify->sendMessage(jsdialog::MessageType::PopupClose, nullptr, 
std::move(pData));
+    mpIdleNotify->sendMessage(jsdialog::MessageType::PopupClose, 
VclPtr<vcl::Window>(nullptr),
+                              std::move(pData));
     flush();
 }
 
+void JSDialogSender::sendMenu(const VclPtr<PopupMenu>& pMenu)
+{
+    if (!mpIdleNotify)
+        return;
+
+    mpIdleNotify->sendMessage(jsdialog::MessageType::Menu, pMenu);
+    mpIdleNotify->Start();
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 7ee79fe30b93..ccfb68ca9f9e 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -191,6 +191,8 @@ std::unique_ptr<weld::Builder> 
Application::CreateBuilder(weld::Widget* pParent,
             return JSInstanceBuilder::CreateSidebarBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId);
         else if (jsdialog::isBuilderEnabledForPopup(rUIFile))
             return JSInstanceBuilder::CreatePopupBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile);
+        else if (jsdialog::isBuilderEnabledForMenu(rUIFile))
+            return JSInstanceBuilder::CreateMenuBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile);
         else if (jsdialog::isBuilderEnabled(rUIFile, bMobile))
             return JSInstanceBuilder::CreateDialogBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile);
     }
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 12c4490aa9df..d61736fac5f4 100644
--- a/vcl/source/window/menu.cxx
+++ b/vcl/source/window/menu.cxx
@@ -2390,6 +2390,27 @@ void Menu::HighlightItem( sal_uInt16 nItemPos )
     }
 }
 
+void Menu::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) const
+{
+    rJsonWriter.put("id", "__MENU__"); // we have single instance of menu at 
the time per session
+    rJsonWriter.put("type", "menu");
+    rJsonWriter.put("count", GetItemCount());
+    {
+        auto aEntries = rJsonWriter.startArray("entries");
+        for (size_t i = 0; i < GetItemCount(); i++)
+        {
+            auto aEntry = rJsonWriter.startStruct();
+            sal_uInt16 nId = GetItemId(i);
+            rJsonWriter.put("row", GetItemIdent(nId));
+            {
+                auto aColumns = rJsonWriter.startArray("columns");
+                auto aColumn = rJsonWriter.startStruct();
+                rJsonWriter.put("text", GetItemText(nId));
+            }
+        }
+    }
+}
+
 MenuBarWindow* MenuBar::getMenuBarWindow()
 {
     // so far just a dynamic_cast, hopefully to be turned into something saner

Reply via email to