include/vcl/jsdialog/executor.hxx    |   10 +++++
 include/vcl/weld.hxx                 |    4 ++
 vcl/inc/jsdialog/jsdialogbuilder.hxx |   16 ++++++++
 vcl/inc/salvtables.hxx               |    5 +-
 vcl/jsdialog/executor.cxx            |   28 ++++++++++++++-
 vcl/jsdialog/jsdialogbuilder.cxx     |   64 +++++++++++++++++++++++++++++++++++
 6 files changed, 124 insertions(+), 3 deletions(-)

New commits:
commit 2d7ab2b66bf1266246fcbab6e428faf0af94d913
Author:     Szymon Kłos <szymon.k...@collabora.com>
AuthorDate: Fri Nov 22 15:04:03 2024 +0100
Commit:     Szymon Kłos <szymon.k...@collabora.com>
CommitDate: Thu Dec 5 22:31:30 2024 +0100

    jsdialog: weld::Menu
    
    - weld::Menu doesn't have base with weld::Widget
    - use separate container, only one menu per WindowId
    - first only send menu and cancel, when user activates
      option we will send another request and execute entry
      as menu can be blocking
    - there is connect_activated way of async setup too
    
    Change-Id: Iea03f82a91ecc19af67b91f85364675c119056ea
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177722
    (cherry picked from commit efeb511607dacce991866bcf328c96a01ab594f9)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177852
    Tested-by: Jenkins
    Reviewed-by: Szymon Kłos <szymon.k...@collabora.com>

diff --git a/include/vcl/jsdialog/executor.hxx 
b/include/vcl/jsdialog/executor.hxx
index ef667a70ce26..260be394d6b8 100644
--- a/include/vcl/jsdialog/executor.hxx
+++ b/include/vcl/jsdialog/executor.hxx
@@ -44,6 +44,16 @@ public:
         rTreeView.signal_row_activated();
     }
 
+    static void trigger_popup_menu(weld::TreeView& rTreeView, const 
CommandEvent& rCommand)
+    {
+        rTreeView.signal_popup_menu(rCommand);
+    }
+
+    static void trigger_activated(weld::Menu& rMenu, const OUString& rIdent)
+    {
+        rMenu.signal_activate(rIdent);
+    }
+
     static void trigger_item_activated(weld::IconView& rIconView)
     {
         rIconView.signal_item_activated();
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index e55cf8a205f0..74290a3c9ec7 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -1009,6 +1009,8 @@ protected:
         return m_aEditingDoneHdl.Call(rIterText);
     }
 
+    void signal_popup_menu(const CommandEvent& rCommand) { 
m_aPopupMenuHdl.Call(rCommand); }
+
     Link<const TreeIter&, OUString> m_aQueryTooltipHdl;
     OUString signal_query_tooltip(const TreeIter& rIter) { return 
m_aQueryTooltipHdl.Call(rIter); }
 
@@ -2449,6 +2451,8 @@ enum class Placement
 
 class VCL_DLLPUBLIC Menu
 {
+    friend class ::LOKTrigger;
+
     Link<const OUString&, void> m_aActivateHdl;
 
 protected:
diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx 
b/vcl/inc/jsdialog/jsdialogbuilder.hxx
index ec8cef53e238..420722c9997b 100644
--- a/vcl/inc/jsdialog/jsdialogbuilder.hxx
+++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx
@@ -309,6 +309,7 @@ public:
     virtual std::unique_ptr<weld::RadioButton> weld_radio_button(const 
OUString& id) override;
     virtual std::unique_ptr<weld::Frame> weld_frame(const OUString& id) 
override;
     virtual std::unique_ptr<weld::MenuButton> weld_menu_button(const OUString& 
id) override;
+    virtual std::unique_ptr<weld::Menu> weld_menu(const OUString& id) override;
     virtual std::unique_ptr<weld::Popover> weld_popover(const OUString& id) 
override;
     virtual std::unique_ptr<weld::Box> weld_box(const OUString& id) override;
     virtual std::unique_ptr<weld::Widget> weld_widget(const OUString& id) 
override;
@@ -330,6 +331,11 @@ public:
     static void ForgetPopup(const OUString& nWindowId);
     static vcl::Window* FindPopup(const OUString& nWindowId);
 
+    // menus in separate container as they don't share base class with 
weld::Widget
+    static void RememberMenu(const OUString& nWindowId, weld::Menu* pMenu);
+    static void ForgetMenu(const OUString& nWindowId);
+    static weld::Menu* FindMenu(const OUString& nWindowId);
+
 private:
     const OUString& GetTypeOfJSON() const;
     VclPtr<vcl::Window>& GetContentWindow();
@@ -871,6 +877,16 @@ public:
     virtual void set_active(bool active) override;
 };
 
+class JSMenu final : public SalInstanceMenu
+{
+public:
+    JSMenu(JSDialogSender* pSender, PopupMenu* pMenu, SalInstanceBuilder* 
pBuilder,
+           bool bTakeOwnership);
+
+    virtual OUString popup_at_rect(weld::Widget* pParent, const 
tools::Rectangle& rRect,
+                                   weld::Placement ePlace = 
weld::Placement::Under) override;
+};
+
 class JSPopover : public JSWidget<SalInstancePopover, DockingWindow>
 {
     vcl::LOKWindowId mnWindowId;
diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 534e158b4196..c183b7fdb57c 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -146,11 +146,12 @@ public:
     virtual ~SalInstanceBuilder() override;
 };
 
-class SAL_DLLPUBLIC_RTTI SalInstanceMenu final : public weld::Menu
+class SAL_DLLPUBLIC_RTTI SalInstanceMenu : public weld::Menu
 {
-private:
+protected:
     VclPtr<PopupMenu> m_xMenu;
 
+private:
     bool m_bTakeOwnership;
     sal_uInt16 m_nLastId;
 
diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx
index c6ab95c293e3..bb000d3daa97 100644
--- a/vcl/jsdialog/executor.cxx
+++ b/vcl/jsdialog/executor.cxx
@@ -74,7 +74,21 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, StringMap
         }
     }
 
-    if (pWidget != nullptr)
+    if (pWidget == nullptr)
+    {
+        // weld::Menu doesn't have base of weld::Widget
+        if (sControlType == "menu")
+        {
+            weld::Menu* pMenu = JSInstanceBuilder::FindMenu(nWindowId);
+            if (pMenu && sAction == "activated")
+            {
+                LOKTrigger::trigger_activated(*pMenu, rData["data"]);
+                return true;
+            }
+        }
+        return false;
+    }
+    else
     {
         // shared actions
 
@@ -558,6 +572,18 @@ bool ExecuteAction(const OUString& nWindowId, const 
OUString& rWidget, StringMap
                     pTreeView->drag_end();
                     return true;
                 }
+                else if (sAction == "contextmenu")
+                {
+                    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);
+
+                    LOKTrigger::trigger_popup_menu(*pTreeView, aCommand);
+                }
             }
         }
         else if (sControlType == "iconview")
diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx
index f5459f2e9a5c..5c1e82397f9f 100644
--- a/vcl/jsdialog/jsdialogbuilder.cxx
+++ b/vcl/jsdialog/jsdialogbuilder.cxx
@@ -13,6 +13,7 @@
 #include <comphelper/lok.hxx>
 #include <iconview.hxx>
 #include <utility>
+#include <vcl/menu.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/toolbox.hxx>
 #include <vcl/toolkit/button.hxx>
@@ -39,6 +40,14 @@ static std::map<OUString, vcl::Window*>& GetLOKPopupsMap()
     return s_aLOKPopupsMap;
 }
 
+static std::map<OUString, weld::Menu*>& GetLOKMenusMap()
+{
+    // Map to remember the LOKWindowId <-> weld menu binding.
+    static std::map<OUString, weld::Menu*> s_aLOKMenusMap;
+
+    return s_aLOKMenusMap;
+}
+
 namespace
 {
 void response_help(vcl::Window* pWindow)
@@ -318,7 +327,10 @@ JSDialogSender::~JSDialogSender() COVERITY_NOEXCEPT_FALSE
 void JSDialogSender::sendFullUpdate(bool bForce)
 {
     if (!mpIdleNotify)
+    {
+        assert(false);
         return;
+    }
 
     if (bForce)
         mpIdleNotify->forceUpdate();
@@ -798,6 +810,28 @@ void JSInstanceBuilder::ForgetPopup(const OUString& 
nWindowId)
         GetLOKPopupsMap().erase(it);
 }
 
+void JSInstanceBuilder::RememberMenu(const OUString& nWindowId, weld::Menu* 
pMenu)
+{
+    GetLOKMenusMap()[nWindowId] = pMenu;
+}
+
+void JSInstanceBuilder::ForgetMenu(const OUString& nWindowId)
+{
+    auto it = GetLOKMenusMap().find(nWindowId);
+    if (it != GetLOKMenusMap().end())
+        GetLOKMenusMap().erase(it);
+}
+
+weld::Menu* JSInstanceBuilder::FindMenu(const OUString& nWindowId)
+{
+    const auto it = GetLOKMenusMap().find(nWindowId);
+
+    if (it != GetLOKMenusMap().end())
+        return it->second;
+
+    return nullptr;
+}
+
 vcl::Window* JSInstanceBuilder::FindPopup(const OUString& nWindowId)
 {
     const auto it = GetLOKPopupsMap().find(nWindowId);
@@ -1201,6 +1235,20 @@ std::unique_ptr<weld::MenuButton> 
JSInstanceBuilder::weld_menu_button(const OUSt
     return pWeldWidget;
 }
 
+std::unique_ptr<weld::Menu> JSInstanceBuilder::weld_menu(const OUString& id)
+{
+    PopupMenu* pPopupMenu = m_xBuilder->get_menu(id);
+
+    JSMenu* pMenu = pPopupMenu ? new JSMenu(this, pPopupMenu, this, false) : 
nullptr;
+
+    std::unique_ptr<weld::Menu> pWeldWidget(pMenu);
+
+    if (pWeldWidget)
+        RememberMenu(getMapIdFromWindowId(), pWeldWidget.get());
+
+    return pWeldWidget;
+}
+
 std::unique_ptr<weld::Popover> JSInstanceBuilder::weld_popover(const OUString& 
id)
 {
     DockingWindow* pDockingWindow = m_xBuilder->get<DockingWindow>(id);
@@ -2348,6 +2396,22 @@ void JSMenuButton::set_active(bool bActive)
     }
 }
 
+JSMenu::JSMenu(JSDialogSender* /*pSender*/, PopupMenu* pPopupMenu, 
SalInstanceBuilder* /*pBuilder*/,
+               bool bTakeOwnership)
+    : SalInstanceMenu(pPopupMenu, bTakeOwnership)
+{
+}
+
+OUString JSMenu::popup_at_rect(weld::Widget* /*pParent*/, const 
tools::Rectangle& /*rRect*/,
+                               weld::Placement /*ePlace*/)
+{
+    // TODO: send message
+
+    // first only send menu and cancel menu
+    // no return SalInstanceMenu::popup_at_rect(pParent, rRect, ePlace);
+    return "";
+}
+
 JSPopover::JSPopover(JSDialogSender* pSender, DockingWindow* pDockingWindow,
                      SalInstanceBuilder* pBuilder, bool bTakeOwnership)
     : JSWidget<SalInstancePopover, DockingWindow>(pSender, pDockingWindow, 
pBuilder, bTakeOwnership)

Reply via email to