Blob Blame History Raw
From c4f94e78c507fc26f56e5c0e8847468ee9e0e2ce Mon Sep 17 00:00:00 2001
From: Maxim Monastirsky <momonasmon@gmail.com>
Date: Sun, 7 Feb 2016 17:53:40 +0200
Subject: [PATCH] native gtk menubars and popup menus
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

tdf#97665 Let's hope that over activation isn't really needed

- MenuBarManager::Activate has a check for duplicate activation,
  which makes the second activation attempt fail. Removing this
  check or deactivating after each activation will likely affect
  performance even more, but on the other hand should solve
  lp#1296715, which was the main reason of the over activation
  in the first place. So let's activate only one menu at a time,
  and do full activation only on the initial update.

- Unfortunately the HUD activation callback doesn't work, so
  we still have to keep active status listener for all menu
  items. (Which is BTW against the recommendation in
  XPopupMenuController::updatePopupMenu IDL doc. Fortunately
  the performance problem hardly noticeable on modern hw.)

Reviewed-on: https://gerrit.libreoffice.org/22369
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Maxim Monastirsky <momonasmon@gmail.com>
(cherry picked from commit 2abdcfd641883f246fe78f2fbe38499c9382c059)

gtk3: some changes towards enabling native gtk3 popup menus

these menubar things can be menu things and can then do
away with the casting, no logic changes intended

(cherry picked from commit c13a0b1f9e76584a4ffaea0ba754c8f9a01793d8)

gtk3: vcl popups flush any unexecuted Select events on popdown

so if the gtksalmenu integration wants to drive popups by setting a selection
on the vcl popup, then the same flush is needed after ShowNativePopupMenu

(cherry picked from commit 3cb62eacae001df546c2a8f39ae4d37c33791d0b)

gtk3: replace old action if same command is added

i.e. originally we preferred the old action, now
prefer the new action because e.g. wrap items in
writer only contain their "checkable" state on
their update

(cherry picked from commit 36bddcbaa2d1673c1331c788eae9534aca2c5ec3)

gtk3: handle items without commands

e.g. the draw/impress context menus. Handle these like
MenuManager::Activate does

(cherry picked from commit b8ee342576b707dbffe877f5c225b640ee65276d)

mark checkable toolbox menu entries as checkable

e.g. the toplevel toolbars put excess entries in
menus. If the entry is not marked as checkable then
a native gtk menu entry will appear to be stateless
when it actually does have a toggle state

(cherry picked from commit 13917e0755bb864f22d0cf75a43854acbdb1eaec)

set gtk layout direction to match ours

(cherry picked from commit b50071c817657866f8b22873be26d34970005a2d)

gtk3: implement native context menus

This reuses lots of the unity machinery which is similar
to the mac concept of a single toplevel menubar.

So to drive popup menus, part of this is a rework that does away with the idea
that the "menubar" is the controller of the hierarchy, and instead the top
element becomes the controller

(cherry picked from commit a0c700b1493c7b51540d1e77b44d1edd9bf920f0)

Resolves: tdf#98636

On changing a menu item from a non-submenu to a submenu then update
the newly created menu as if it was the first full update of
the entire menu hierarchy.

On changing a menu item from a submenu to a non-submenu its evidentially
not sufficient to unset the G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION attribute
so remove the submenu-item and add a new non-submenu item to force
its new type

gtk3: separate the two different usages of Activate

(cherry picked from commit d4c7f26971cb20f1a0b8a184cd6a00129a838dac)

gtk3: put all of our existing toplevel stuff inside a GtkGrid

so we can optionally insert a native menubar above it all

(cherry picked from commit 7f39d6831e03cbea408ff499df1c5b120d825cee)

gtk3: native gtk menubar

(cherry picked from commit 800704e0c46f6f86f6a18a477ba4b6f313c5693a)

gtk3: add a menubar close button

the css foo is modeled on the similiar gedit close buttons
in its tabs

(cherry picked from commit 3ae6c7fcbe14f7a425fa1db2cb388b12848be65b)

gtk3+wayland: gnome#762756 hack around keyboard focus

(cherry picked from commit 1724081f6363fb2b97523a2c5edb1424d7959f8b)

shouldn't have to recall gtk_init here

(cherry picked from commit 2ba53aee16c32a0979343cddf3ea2538d9bb4732)

gtk3: menubar close button shouldn't get keyboard focus

(cherry picked from commit 3a4b68260bc6c1e8bcb694f640c70652b830c621)

extra menubar displayed after exiting embedded object edit

when using native gtk3 menubars.

The issue is that MenuBarManager does not own its MenuBar.
And in this embedded menubar situation a new menubar is newed and passed to
m_pInplaceMenuBar but nothing destroys it.

Now with native gtk3 menubars this becomes obvious as the native menubar stays
behind, while in the non-native case the old menubar is replaced by
the new one so while it still leaks the menubar you don't see it.

(cherry picked from commit 0c622c988523da4edc68d68ca4f4358c1fef83e4)

refactor these two bizarro positioning things into reusable chunks

(cherry picked from commit fbea89b6b2a4a91fecc4200d59bf03166c3f8908)

gtk3: position the native popup menus exactly where we want them

(cherry picked from commit daa7754026ba5b025076b90f7a4427cd5820e736)

Resolves: tdf#98473 replace || with ‖ to avoid illegal gtk3 action names

(cherry picked from commit 032cb7ed80a504b4abec479f30c2c03f10e14639)

Resolves: tdf#92067 and tdf#99599, use a foolproof action naming scheme

encode the GtkSalMenu and the item id into the action_name so
each one is unique and directly refers to the menu and item in the
menu so knowing which one is which is direct and simple

(cherry picked from commit 27014f563577c3c5da19e37a57d4e73c0ebae140)

gtk3: getting new entries added after the menu is visible is problematic

(cherry picked from commit 945064c0b7ad4da6e215668ae7e4edb14945f00c)

reroute UpdateFull through ActivateAllSubmenus

(cherry picked from commit b41a5e899bcb567595f489fab37cbebcc5efacc0)

Resolves: tdf#98636 if the menubar hierarchy has been changed

then update the whole thing by re-calling SetFrame

(cherry picked from commit d20e08a3ab819ac24f7ea49a98b4dd3683120857)

Resolves: tdf#99857 missing items from toolbar right click

we do an activate/deactivate to force the vcl menu to update
so we have a full model before we try to show the gtk one.

This particular toplevel menu has a deactivate handler which
empties out the submenus on the deactivate, so we don't get
the full thing, so we end up with missing entries.

The Deactivate to empty the menu is a bit dubious. Even if
we limited the activate/deactivate to submenus we still
get the deactivate before the "select" so the ordering is
fragile.

The Deactivate handler seems dubious anyway, why not just
clear it out after the Execute has finished which is
simpler and more direct anyway

dde83e3cea5b5fc1f91ebd336a2071ce8ff75e75

(cherry picked from commit a30e3ea231dd1a355e616fed33eb7c4c4866c12c)

it would seem safer to update before HandleMenuDeActivateEvent

i.e. Activate, Update and Deactivate

(cherry picked from commit 8c82dfe085ec0a7c27123927743387ecd8406846)

Related: tdf#92695 gtk3 only activate/deactive submenus, not the toplevel

(cherry picked from commit ba4e50c856e5279c05b90297660b396868a6d815)
(cherry picked from commit dbf99bb2e092ec5dca6b405e15c507f66ad0bc6d)

Related: tdf#92695 we already have ImplGetItem from mnHighItemId here

no logic change intended

(cherry picked from commit c380f0fc125f50ad8efca2ce032d3d2a67d78f0a)
(cherry picked from commit df668868917d1dac11d49f1f650c43666fadea54)

Resolves: tdf#92695 protect both branches against missing ToolItem

(cherry picked from commit ab0dc9524a36a394e97df9499bf1f5e4b94cfdca)
(cherry picked from commit c845c7bf597caa11b1617ab71029c499819028bc)

Change-Id: I96affa72412f3f38160fdca4b6efd20ca68d059f
bb1b5354d5e1483327f172d6890e134f1e4b9ee4
59be60de5742d1e382cabefcbf0d8cdd5fc30b00
6a6ce94126253396cc273834a7e8a4fb0a56921d
02a0e377a2d3a57ac7ac9239aaa75dbb856489d2
7168b44d59fd64dfe264ed8ca26355252d697251
27610f28f42368355bef1b3461fc3ccea1b07218
4336391718844bc73cfc47c1043f99f0e3b812d8
2030d9198d6849643a5991ddfffc1cc3425ba72e
c7442209efac40bd0cd034ff006e87128b0bea35
b8486d7164493db79c868715a2aef209d7472c01
bb75aa3eae99e499e1c2026c41b0bf51d7f8cf31
7af32ad0434a49c52eea215f797c502be7f96dac
6be61592a16660d62be583e082ffcf5b1907770f
f851dbdb034384395ba590fda61f3383f5ef791a
6ddd512afa1e41d2dec7c92f61d65ed5bbfa9ace
d732cb66664a71efc471d7bad35f4de890e1017e
3f89aca650d31658ce17b3b1496a7babba23bdc6
2594925cba1f7c9c90178906d9c782024ad1cf4f
c5098c439ac94d68f881e06378938fab29e8bb3d
81bb278e73946f864e29aeab884e07e16835dad3
bc2fc35c3d5315eb7d25181c2c2eba4cb5509a96
8689abacbfc970a9124bb97a3962bcfb0df9c67d
b16006a76ca04dc104232a056c43fda2b5b24074
56145f6236db1787cc4ee623c513cb927bf2a972
707f6801fca39bb767e49d6af0b9b3ceccb25e94
5ce7ae39e7db62551733a005f3163ebfbb027af6
a53a21db56c857e1274c60f846fc955fef9e3dfb
de54fddf7b217e65a405bd80853d5302a419f046
---
 framework/inc/uielement/toolbarmanager.hxx       |   2 +-
 framework/source/layoutmanager/layoutmanager.cxx |  57 +-
 framework/source/uielement/menubarmanager.cxx    |  11 +-
 framework/source/uielement/toolbarmanager.cxx    |  19 +-
 include/vcl/floatwin.hxx                         |   4 +-
 include/vcl/menu.hxx                             |  18 +-
 l10ntools/source/merge.cxx                       |  17 +-
 vcl/inc/salmenu.hxx                              |   2 +
 vcl/inc/unx/gtk/gloactiongroup.h                 |   3 +
 vcl/inc/unx/gtk/gtkframe.hxx                     |   9 +-
 vcl/inc/unx/gtk/gtksalmenu.hxx                   |  42 +-
 vcl/source/app/salvtables.cxx                    |   4 +
 vcl/source/window/floatwin.cxx                   |  54 +-
 vcl/source/window/menu.cxx                       |  85 +--
 vcl/source/window/menubarwindow.cxx              |   2 +
 vcl/source/window/toolbox.cxx                    |  15 +-
 vcl/source/window/toolbox2.cxx                   |  20 +-
 vcl/unx/gtk/a11y/atklistener.cxx                 |   1 -
 vcl/unx/gtk/gloactiongroup.cxx                   |  64 +--
 vcl/unx/gtk/glomenu.cxx                          |   1 +
 vcl/unx/gtk/gtkdata.cxx                          |  19 +-
 vcl/unx/gtk/gtksalframe.cxx                      |  21 +-
 vcl/unx/gtk/gtksalmenu.cxx                       | 638 +++++++++++++++--------
 vcl/unx/gtk3/gtk3gtkframe.cxx                    |  35 +-
 vcl/unx/gtk3/gtk3salnativewidgets-gtk.cxx        |   1 -
 25 files changed, 653 insertions(+), 491 deletions(-)

diff --git a/framework/inc/uielement/toolbarmanager.hxx b/framework/inc/uielement/toolbarmanager.hxx
index f109f14..1efcde3 100644
--- a/framework/inc/uielement/toolbarmanager.hxx
+++ b/framework/inc/uielement/toolbarmanager.hxx
@@ -128,7 +128,7 @@ class ToolBarManager : public ToolbarManager_Base
 
         DECL_LINK_TYPED( MenuButton, ToolBox *, void );
         DECL_LINK_TYPED( MenuSelect, Menu *, bool );
-        DECL_LINK_TYPED( MenuDeactivate, Menu *, bool );
+        void MenuDeactivated();
         DECL_LINK_TYPED(AsyncUpdateControllersHdl, Timer *, void);
         DECL_STATIC_LINK_TYPED( ToolBarManager, ExecuteHdl_Impl, void*, void );
 
diff --git a/framework/source/layoutmanager/layoutmanager.cxx b/framework/source/layoutmanager/layoutmanager.cxx
index eb66efe..43a529a 100644
--- a/framework/source/layoutmanager/layoutmanager.cxx
+++ b/framework/source/layoutmanager/layoutmanager.cxx
@@ -210,12 +210,18 @@ void LayoutManager::impl_clearUpMenuBar()
     }
 
     // reset inplace menubar manager
-    m_pInplaceMenuBar = nullptr;
+    Menu *pMenuBar = nullptr;
+    if (m_pInplaceMenuBar)
+    {
+        pMenuBar = m_pInplaceMenuBar->GetMenuBar();
+        m_pInplaceMenuBar = nullptr;
+    }
     if ( m_xInplaceMenuBar.is() )
     {
         m_xInplaceMenuBar->dispose();
         m_xInplaceMenuBar.clear();
     }
+    delete pMenuBar;
 
     Reference< XComponent > xComp( m_xMenuBar, UNO_QUERY );
     if ( xComp.is() )
@@ -1148,10 +1154,19 @@ throw (uno::RuntimeException, std::exception)
         SolarMutexGuard aGuard;
 
         // Reset old inplace menubar!
-        m_pInplaceMenuBar = nullptr;
-        if ( m_xInplaceMenuBar.is() )
+        Menu *pOldMenuBar = nullptr;
+        if (m_pInplaceMenuBar)
+        {
+            pOldMenuBar = m_pInplaceMenuBar->GetMenuBar();
+            m_pInplaceMenuBar = nullptr;
+        }
+        if (m_xInplaceMenuBar.is())
+        {
             m_xInplaceMenuBar->dispose();
-        m_xInplaceMenuBar.clear();
+            m_xInplaceMenuBar.clear();
+        }
+        delete pOldMenuBar;
+
         m_bInplaceMenuSet = false;
 
         if ( m_xFrame.is() && m_xContainerWindow.is() )
@@ -1199,10 +1214,18 @@ throw (uno::RuntimeException)
     }
 
     // Remove inplace menu bar
-    m_pInplaceMenuBar = nullptr;
-    if ( m_xInplaceMenuBar.is() )
+    Menu *pMenuBar = nullptr;
+    if (m_pInplaceMenuBar)
+    {
+        pMenuBar = m_pInplaceMenuBar->GetMenuBar();
+        m_pInplaceMenuBar = nullptr;
+    }
+    if (m_xInplaceMenuBar.is())
+    {
         m_xInplaceMenuBar->dispose();
-    m_xInplaceMenuBar.clear();
+        m_xInplaceMenuBar.clear();
+    }
+    delete pMenuBar;
 }
 
 void SAL_CALL LayoutManager::attachFrame( const Reference< XFrame >& xFrame )
@@ -2807,12 +2830,18 @@ throw( RuntimeException, std::exception )
         implts_destroyElements();
         impl_clearUpMenuBar();
         m_xMenuBar.clear();
-        if ( m_xInplaceMenuBar.is() )
+        Menu *pMenuBar = nullptr;
+        if (m_pInplaceMenuBar)
         {
+            pMenuBar = m_pInplaceMenuBar->GetMenuBar();
             m_pInplaceMenuBar = nullptr;
+        }
+        if (m_xInplaceMenuBar.is())
+        {
             m_xInplaceMenuBar->dispose();
+            m_xInplaceMenuBar.clear();
         }
-        m_xInplaceMenuBar.clear();
+        delete pMenuBar;
         m_xContainerWindow.clear();
         m_xContainerTopWindow.clear();
 
@@ -2865,12 +2894,18 @@ throw( RuntimeException, std::exception )
         }
         impl_clearUpMenuBar();
         m_xMenuBar.clear();
-        if ( m_xInplaceMenuBar.is() )
+        Menu *pMenuBar = nullptr;
+        if (m_pInplaceMenuBar)
         {
+            pMenuBar = m_pInplaceMenuBar->GetMenuBar();
             m_pInplaceMenuBar = nullptr;
+        }
+        if (m_xInplaceMenuBar.is())
+        {
             m_xInplaceMenuBar->dispose();
+            m_xInplaceMenuBar.clear();
         }
-        m_xInplaceMenuBar.clear();
+        delete pMenuBar;
         m_xContainerWindow.clear();
         m_xContainerTopWindow.clear();
     }
diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx
index 1c0535f..08cd8bb 100644
--- a/framework/source/uielement/menubarmanager.cxx
+++ b/framework/source/uielement/menubarmanager.cxx
@@ -386,10 +386,6 @@ throw ( RuntimeException, std::exception )
     OUString aFeatureURL = Event.FeatureURL.Complete;
 
     SolarMutexGuard aSolarGuard;
-    if ( m_bHasMenuBar )
-    {
-        vcl::MenuInvalidator::Invalidated();
-    }
     {
         if ( m_bDisposed )
             return;
@@ -488,6 +484,8 @@ throw ( RuntimeException, std::exception )
                 pMenuItemHandler->xMenuItemDispatch.clear();
             }
         }
+        if ( m_bHasMenuBar && !m_bActive )
+            m_pVCLMenu->UpdateNativeMenu();
     }
 }
 
@@ -894,9 +892,8 @@ IMPL_LINK_TYPED( MenuBarManager, Activate, Menu *, pMenu, bool )
                                 if ( !bPopupMenu )
                                 {
                                     xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
-                                    xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
-                                    if ( m_bHasMenuBar )
-                                        xMenuItemDispatch->addStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
+                                    if ( !m_bHasMenuBar )
+                                        xMenuItemDispatch->removeStatusListener( static_cast< XStatusListener* >( this ), aTargetURL );
                                 }
                             }
                             else if ( !bPopupMenu )
diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx
index 77849d5..1a9f9d3 100644
--- a/framework/source/uielement/toolbarmanager.cxx
+++ b/framework/source/uielement/toolbarmanager.cxx
@@ -1349,19 +1349,11 @@ void ToolBarManager::ImplClearPopupMenu( ToolBox *pToolBar )
     }
 }
 
-IMPL_LINK_TYPED( ToolBarManager, MenuDeactivate, Menu*, pMenu, bool )
+void ToolBarManager::MenuDeactivated()
 {
-    SolarMutexGuard g;
-
-    if ( m_bDisposed )
-        return true;
-
-    if( pMenu != m_pToolBar->GetMenu() )
-        return true;
-
-    ImplClearPopupMenu( m_pToolBar );
-
-    return false;
+    if (m_bDisposed)
+        return;
+    ImplClearPopupMenu(m_pToolBar);
 }
 
 Reference< XModel > ToolBarManager::GetModelFromFrame() const
@@ -1557,7 +1549,6 @@ IMPL_LINK_TYPED( ToolBarManager, Command, CommandEvent const *, pCmdEvt, void )
         // when the menu is being used as an overflow menu.
         Menu *pManagerMenu = m_pToolBar->GetMenu();
         pManagerMenu->SetSelectHdl( LINK( this, ToolBarManager, MenuSelect ) );
-        pManagerMenu->SetDeactivateHdl( LINK( this, ToolBarManager, MenuDeactivate ) );
 
         // make sure all disabled entries will be shown
         pMenu->SetMenuFlags( pMenu->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
@@ -1571,7 +1562,7 @@ IMPL_LINK_TYPED( ToolBarManager, Command, CommandEvent const *, pCmdEvt, void )
         {
             // Unlink our listeners again -- see above for why.
             pManagerMenu->SetSelectHdl( Link<Menu*, bool>() );
-            pManagerMenu->SetDeactivateHdl( Link<Menu *, bool>() );
+            MenuDeactivated();
         }
     }
 }
diff --git a/include/vcl/floatwin.hxx b/include/vcl/floatwin.hxx
index d999e65..6ef1280 100644
--- a/include/vcl/floatwin.hxx
+++ b/include/vcl/floatwin.hxx
@@ -131,9 +131,11 @@ public:
     SAL_DLLPRIVATE bool             ImplIsFloatPopupModeWindow( const vcl::Window* pWindow );
     SAL_DLLPRIVATE void             ImplSetMouseDown() { mbMouseDown = true; }
     SAL_DLLPRIVATE bool             ImplIsMouseDown() const  { return mbMouseDown; }
-    SAL_DLLPRIVATE static Point     ImplCalcPos( vcl::Window* pWindow,
+                   static Point     ImplCalcPos( vcl::Window* pWindow,
                                                  const Rectangle& rRect, FloatWinPopupFlags nFlags,
                                                  sal_uInt16& rArrangeIndex );
+                   static Point     ImplConvertToAbsPos(vcl::Window* pReference, const Point& rPos);
+                   static Rectangle ImplConvertToAbsPos(vcl::Window* pReference, const Rectangle& rRect);
     SAL_DLLPRIVATE void             ImplEndPopupMode( FloatWinPopupEndFlags nFlags = FloatWinPopupEndFlags::NONE, sal_uLong nFocusId = 0 );
     SAL_DLLPRIVATE Rectangle&       ImplGetItemEdgeClipRect();
     SAL_DLLPRIVATE bool             ImplIsInPrivatePopupMode() const { return mbInPopupMode; }
diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx
index ade703b..f10141b 100644
--- a/include/vcl/menu.hxx
+++ b/include/vcl/menu.hxx
@@ -313,6 +313,8 @@ public:
     void RemoveDisabledEntries( bool bCheckPopups = true, bool bRemoveEmptyPopups = false );
     bool HasValidEntries( bool bCheckPopups = true );
 
+    void UpdateNativeMenu();
+
     void SetItemText( sal_uInt16 nItemId, const OUString& rStr );
     OUString GetItemText( sal_uInt16 nItemId ) const;
 
@@ -404,15 +406,11 @@ public:
 
     void HighlightItem( sal_uInt16 nItemPos );
     void DeHighlight() { HighlightItem( 0xFFFF ); } // MENUITEMPOS_INVALID
-};
-
 
-namespace vcl { namespace MenuInvalidator {
-
-VCL_DLLPUBLIC void AddMenuInvalidateListener(const Link<LinkParamNone*,void>&);
-VCL_DLLPUBLIC void Invalidated();
-
-}}
+    bool HandleMenuCommandEvent(Menu *pMenu, sal_uInt16 nEventId) const;
+    bool HandleMenuActivateEvent(Menu *pMenu) const;
+    bool HandleMenuDeActivateEvent(Menu *pMenu) const;
+};
 
 class VCL_DLLPUBLIC MenuBar : public Menu
 {
@@ -463,10 +461,7 @@ public:
     void ShowButtons( bool bClose, bool bFloat, bool bHide );
 
     virtual void SelectItem(sal_uInt16 nId) override;
-    bool HandleMenuActivateEvent(Menu *pMenu) const;
-    bool HandleMenuDeActivateEvent(Menu *pMenu) const;
     bool HandleMenuHighlightEvent(Menu *pMenu, sal_uInt16 nEventId) const;
-    bool HandleMenuCommandEvent(Menu *pMenu, sal_uInt16 nEventId) const;
     bool HandleMenuButtonEvent(Menu *pMenu, sal_uInt16 nEventId);
 
     void SetCloseButtonClickHdl( const Link<void*,void>& rLink ) { maCloseHdl = rLink; }
@@ -520,6 +515,7 @@ private:
 
 protected:
     SAL_DLLPRIVATE sal_uInt16 ImplExecute( vcl::Window* pWindow, const Rectangle& rRect, FloatWinPopupFlags nPopupFlags, Menu* pStaredFrom, bool bPreSelectFirst );
+    SAL_DLLPRIVATE void ImplFlushPendingSelect();
     SAL_DLLPRIVATE long ImplCalcHeight( sal_uInt16 nEntries ) const;
     SAL_DLLPRIVATE sal_uInt16 ImplCalcVisEntries( long nMaxHeight, sal_uInt16 nStartEntry = 0, sal_uInt16* pLastVisible = nullptr ) const;
 
diff --git a/l10ntools/source/merge.cxx b/l10ntools/source/merge.cxx
index dd6639b..765f988 100644
--- a/l10ntools/source/merge.cxx
+++ b/l10ntools/source/merge.cxx
@@ -123,13 +123,22 @@ bool MergeEntrys::GetText( OString &rReturn,
     return bReturn;
 }
 
+namespace
+{
+    OString GetDoubleBars()
+    {
+        //DOUBLE VERTICAL LINE instead of || because the translations make their
+        //way into action_names under gtk3 where || is illegal
+        return OUStringToOString(OUString(static_cast<sal_Unicode>(0x2016)), RTL_TEXTENCODING_UTF8);
+    }
+}
 
 OString MergeEntrys::GetQTZText(const ResData& rResData, const OString& rOrigText)
 {
     const OString sFilename = rResData.sFilename.copy(rResData.sFilename.lastIndexOf('/')+1);
     const OString sKey =
         PoEntry::genKeyId(sFilename + rResData.sGId + rResData.sId + rResData.sResTyp + rOrigText);
-    return sKey + "||" + rOrigText;
+    return sKey + GetDoubleBars() + rOrigText;
 }
 
 
@@ -423,9 +432,9 @@ void MergeDataFile::InsertEntry(
         const OString sTemp = rInFilename + rGID + rLID + rTYP;
         pMergeEntrys->InsertEntry(
             nLANG,
-            rTEXT.isEmpty()? rTEXT : PoEntry::genKeyId(sTemp + rTEXT) + "||" + rTEXT,
-            rQHTEXT.isEmpty()? rQHTEXT : PoEntry::genKeyId(sTemp + rQHTEXT) + "||" + rQHTEXT,
-            rTITLE.isEmpty()? rTITLE : PoEntry::genKeyId(sTemp + rTITLE) + "||" + rTITLE );
+            rTEXT.isEmpty()? rTEXT : PoEntry::genKeyId(sTemp + rTEXT) + GetDoubleBars() + rTEXT,
+            rQHTEXT.isEmpty()? rQHTEXT : PoEntry::genKeyId(sTemp + rQHTEXT) + GetDoubleBars() + rQHTEXT,
+            rTITLE.isEmpty()? rTITLE : PoEntry::genKeyId(sTemp + rTITLE) + GetDoubleBars() + rTITLE );
     }
     else
     {
diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx
index 287e19e..40f5fe0 100644
--- a/vcl/inc/salmenu.hxx
+++ b/vcl/inc/salmenu.hxx
@@ -78,8 +78,10 @@ public:
     virtual void SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const vcl::KeyCode& rKeyCode, const OUString& rKeyName ) = 0;
     virtual void GetSystemMenuData( SystemMenuData* pData ) = 0;
     virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, FloatWinPopupFlags nFlags);
+    virtual void ShowCloseButton(bool bShow);
     virtual bool AddMenuBarButton( const SalMenuButtonItem& ); // return false if not implemented or failure
     virtual void RemoveMenuBarButton( sal_uInt16 nId );
+    virtual void Update() {}
 
     // TODO: implement show/hide for the Win/Mac VCL native backends
     virtual void ShowItem( unsigned nPos, bool bShow ) { EnableItem( nPos, bShow ); }
diff --git a/vcl/inc/unx/gtk/gloactiongroup.h b/vcl/inc/unx/gtk/gloactiongroup.h
index 080b679..ec6bd39 100644
--- a/vcl/inc/unx/gtk/gloactiongroup.h
+++ b/vcl/inc/unx/gtk/gloactiongroup.h
@@ -46,6 +46,9 @@ GType               g_lo_action_group_get_type              (void) G_GNUC_CONST;
 
 GLOActionGroup *    g_lo_action_group_new                   (gpointer           frame);
 
+void                g_lo_action_group_set_top_menu          (GLOActionGroup     *group,
+                                                             gpointer           top_menu);
+
 void                g_lo_action_group_insert                (GLOActionGroup     *group,
                                                              const gchar        *action_name,
                                                              gint                item_id,
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 1d8334c..6a011cb 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -170,6 +170,9 @@ class GtkSalFrame : public SalFrame
 
     SalX11Screen                    m_nXScreen;
     GtkWidget*                      m_pWindow;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkGrid*                        m_pTopLevelGrid;
+#endif
     GtkEventBox*                    m_pEventBox;
     GtkFixed*                       m_pFixedContainer;
     GdkWindow*                      m_pForeignParent;
@@ -225,10 +228,7 @@ class GtkSalFrame : public SalFrame
     SalMenu*                        m_pSalMenu;
 
 #if defined(ENABLE_DBUS) && ENABLE_GIO
-    public:
-    void EnsureDbusMenuSynced();
     private:
-    SalMenu*                        m_pLastSyncedDbusMenu;
     friend void ensure_dbus_setup(GdkWindow* gdkWindow, GtkSalFrame* pSalFrame);
     friend void on_registrar_available (GDBusConnection*, const gchar*, const gchar*, gpointer);
     friend void on_registrar_unavailable (GDBusConnection*, const gchar*, gpointer);
@@ -371,6 +371,9 @@ public:
     GtkWidget*  getWindow() const { return m_pWindow; }
     GtkFixed*   getFixedContainer() const { return m_pFixedContainer; }
     GtkWidget*  getMouseEventWidget() const;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkGrid*    getTopLevelGridWidget() const { return m_pTopLevelGrid; }
+#endif
     GdkWindow*  getForeignParent() const { return m_pForeignParent; }
     GdkNativeWindow getForeignParentWindow() const { return m_aForeignParentWindow; }
     GdkWindow*  getForeignTopLevel() const { return m_pForeignTopLevel; }
diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx
index 011c3e4..b65f0a5 100644
--- a/vcl/inc/unx/gtk/gtksalmenu.hxx
+++ b/vcl/inc/unx/gtk/gtksalmenu.hxx
@@ -19,6 +19,7 @@
 
 #include <unx/salmenu.h>
 #include <unx/gtk/gtkframe.hxx>
+#include <vcl/idle.hxx>
 
 #if defined(ENABLE_DBUS) && ENABLE_GIO && \
     (GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 36)
@@ -41,19 +42,25 @@ class GtkSalMenu : public SalMenu
 {
 private:
     std::vector< GtkSalMenuItem* >  maItems;
+    Idle                            maUpdateMenuBarIdle;
 
-    bool                        mbMenuBar;
+    bool                            mbMenuBar;
+    bool                            mbUnityMode;
+    bool                            mbNeedsUpdate;
+    GtkWidget*                      mpMenuBarWidget;
+    GtkWidget*                      mpCloseButton;
     Menu*                           mpVCLMenu;
     GtkSalMenu*                     mpParentSalMenu;
-    const GtkSalFrame*              mpFrame;
+    GtkSalFrame*                    mpFrame;
 
     // GMenuModel and GActionGroup attributes
     GMenuModel*                     mpMenuModel;
     GActionGroup*                   mpActionGroup;
 
-    GtkSalMenu*                 GetMenuForItemCommand( gchar* aCommand, int& rDupsToSkip, gboolean bGetSubmenu );
-    void                        ImplUpdate( gboolean bRecurse );
-    void                        ActivateAllSubmenus(MenuBar* pMenuBar);
+    void                        ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries);
+    void                        ActivateAllSubmenus(Menu* pMenuBar);
+
+    DECL_LINK_TYPED(MenuBarHierarchyChangeHandler, Idle*, void);
 
 public:
     GtkSalMenu( bool bMenuBar );
@@ -77,14 +84,14 @@ public:
 
     void                        SetMenu( Menu* pMenu ) { mpVCLMenu = pMenu; }
     Menu*                       GetMenu() { return mpVCLMenu; }
-    void                        SetMenuModel( GMenuModel* pMenuModel ) { mpMenuModel = pMenuModel; }
+    void                        SetMenuModel(GMenuModel* pMenuModel);
     unsigned                    GetItemCount() { return maItems.size(); }
     GtkSalMenuItem*             GetItemAtPos( unsigned nPos ) { return maItems[ nPos ]; }
     void                        SetActionGroup( GActionGroup* pActionGroup ) { mpActionGroup = pActionGroup; }
     bool                        IsItemVisible( unsigned nPos );
 
     void                        NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText );
-    void                        NativeSetItemCommand( unsigned nSection,
+    bool                        NativeSetItemCommand( unsigned nSection,
                                                       unsigned nItemPos,
                                                       sal_uInt16 nId,
                                                       const gchar* aCommand,
@@ -95,13 +102,22 @@ public:
     void                        NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck );
     void                        NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName );
 
-    void                        DispatchCommand( gint itemId, const gchar* aCommand );
-    void                        Activate();
-    void                        Deactivate( const gchar* aMenuCommand );
-    void                        Display( bool bVisible );
+    static void                 DispatchCommand(const gchar* pMenuCommand);
+    static void                 Activate(const gchar* pMenuCommand);
+    static void                 Deactivate(const gchar* pMenuCommand);
+    void                        EnableUnity(bool bEnable);
     bool                        PrepUpdate();
-    void                        Update();           // Update this menu only.
-    void                        UpdateFull();       // Update full menu hierarchy from this menu.
+    virtual void                Update() override;  // Update this menu only.
+    // Update full menu hierarchy from this menu.
+    void                        UpdateFull () { ActivateAllSubmenus(mpVCLMenu); Update(); }
+    GtkSalMenu*                 GetTopLevel();
+    void                        SetNeedsUpdate();
+
+    void CreateMenuBarWidget();
+    void DestroyMenuBarWidget();
+
+    virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, FloatWinPopupFlags nFlags) override;
+    virtual void ShowCloseButton(bool bShow) override;
 };
 
 class GtkSalMenuItem : public SalMenuItem
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index c540eef5..c135cdf 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -135,6 +135,10 @@ bool SalMenu::ShowNativePopupMenu(FloatingWindow *, const Rectangle&, FloatWinPo
     return false;
 }
 
+void SalMenu::ShowCloseButton(bool)
+{
+}
+
 bool SalMenu::AddMenuBarButton( const SalMenuButtonItem& )
 {
     return false;
diff --git a/vcl/source/window/floatwin.cxx b/vcl/source/window/floatwin.cxx
index b5c038c..8dfc3f4 100644
--- a/vcl/source/window/floatwin.cxx
+++ b/vcl/source/window/floatwin.cxx
@@ -450,10 +450,8 @@ Point FloatingWindow::ImplCalcPos( vcl::Window* pWindow,
     return pW->OutputToScreenPixel( aPos );
 }
 
-FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const Point& rPos, HitTest& rHitTest )
+Point FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const Point& rPos)
 {
-    FloatingWindow* pWin = this;
-
     Point aAbsolute( rPos );
 
     const OutputDevice *pWindowOutDev = pReference->GetOutDev();
@@ -473,6 +471,37 @@ FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const
         aAbsolute = Point( pReference->OutputToAbsoluteScreenPixel(
             pReference->ScreenToOutputPixel(rPos) ) );
 
+    return aAbsolute;
+}
+
+Rectangle FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const Rectangle& rRect)
+{
+    Rectangle aFloatRect = rRect;
+
+    const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
+
+    // compare coordinates in absolute screen coordinates
+    // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
+    if( pReference->HasMirroredGraphics()  )
+    {
+        if(!pReference->IsRTLEnabled() )
+            // --- RTL --- re-mirror back to get device coordinates
+            pParentWinOutDev->ReMirror(aFloatRect);
+
+        aFloatRect.SetPos(pReference->ScreenToOutputPixel(aFloatRect.TopLeft()));
+        aFloatRect = pReference->ImplOutputToUnmirroredAbsoluteScreenPixel(aFloatRect);
+    }
+    else
+        aFloatRect.SetPos(pReference->OutputToAbsoluteScreenPixel(pReference->ScreenToOutputPixel(rRect.TopLeft())));
+    return aFloatRect;
+}
+
+FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const Point& rPos, HitTest& rHitTest )
+{
+    FloatingWindow* pWin = this;
+
+    Point aAbsolute(FloatingWindow::ImplConvertToAbsPos(pReference, rPos));
+
     do
     {
         // compute the floating window's size in absolute screen coordinates
@@ -666,24 +695,7 @@ void FloatingWindow::StartPopupMode( const Rectangle& rRect, FloatWinPopupFlags
     // convert maFloatRect to absolute device coordinates
     // so they can be compared across different frames
     // !!! rRect is expected to be in screen coordinates of the parent frame window !!!
-    maFloatRect             = rRect;
-
-    vcl::Window *pReference =  GetParent();
-    const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
-
-    // compare coordinates in absolute screen coordinates
-    // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
-    if( pReference->HasMirroredGraphics()  )
-    {
-        if(!pReference->IsRTLEnabled() )
-            // --- RTL --- re-mirror back to get device coordinates
-            pParentWinOutDev->ReMirror(maFloatRect);
-
-        maFloatRect.SetPos(pReference->ScreenToOutputPixel(maFloatRect.TopLeft()));
-        maFloatRect = pReference->ImplOutputToUnmirroredAbsoluteScreenPixel(maFloatRect);
-    }
-    else
-        maFloatRect.SetPos(pReference->OutputToAbsoluteScreenPixel(pReference->ScreenToOutputPixel(rRect.TopLeft())));
+    maFloatRect = FloatingWindow::ImplConvertToAbsPos(GetParent(), rRect);
 
     maFloatRect.Left()     -= 2;
     maFloatRect.Top()      -= 2;
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 3a6e54c..b753fac 100644
--- a/vcl/source/window/menu.cxx
+++ b/vcl/source/window/menu.cxx
@@ -2314,6 +2314,12 @@ sal_uLong Menu::DeactivateMenuBar(sal_uLong nFocusId)
     return nFocusId;
 }
 
+void Menu::UpdateNativeMenu()
+{
+    if ( ImplGetSalMenu() )
+        ImplGetSalMenu()->Update();
+}
+
 void Menu::MenuBarKeyInput(const KeyEvent&)
 {
 }
@@ -2673,14 +2679,13 @@ void MenuBar::SelectItem(sal_uInt16 nId)
 }
 
 // handler for native menu selection and command events
-
-bool MenuBar::HandleMenuActivateEvent( Menu *pMenu ) const
+bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
 {
     if( pMenu )
     {
         ImplMenuDelData aDelData( this );
 
-        pMenu->pStartedFrom = const_cast<MenuBar*>(this);
+        pMenu->pStartedFrom = const_cast<Menu*>(this);
         pMenu->bInCallback = true;
         pMenu->Activate();
 
@@ -2690,13 +2695,13 @@ bool MenuBar::HandleMenuActivateEvent( Menu *pMenu ) const
     return true;
 }
 
-bool MenuBar::HandleMenuDeActivateEvent( Menu *pMenu ) const
+bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
 {
     if( pMenu )
     {
         ImplMenuDelData aDelData( this );
 
-        pMenu->pStartedFrom = const_cast<MenuBar*>(this);
+        pMenu->pStartedFrom = const_cast<Menu*>(this);
         pMenu->bInCallback = true;
         pMenu->Deactivate();
         if( !aDelData.isDeleted() )
@@ -2729,14 +2734,14 @@ bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventI
         return false;
 }
 
-bool MenuBar::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
+bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
 {
     if( !pMenu )
-        pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nCommandEventId);
+        pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
     if( pMenu )
     {
         pMenu->nSelectedId = nCommandEventId;
-        pMenu->pStartedFrom = const_cast<MenuBar*>(this);
+        pMenu->pStartedFrom = const_cast<Menu*>(this);
         pMenu->ImplSelect();
         return true;
     }
@@ -2928,6 +2933,19 @@ sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Rectangle& rRect,
     return ImplExecute( pExecWindow, rRect, nPopupModeFlags, nullptr, false );
 }
 
+void PopupMenu::ImplFlushPendingSelect()
+{
+    // is there still Select?
+    Menu* pSelect = ImplFindSelectMenu();
+    if (pSelect)
+    {
+        // Select should be called prior to leaving execute in a popup menu!
+        Application::RemoveUserEvent( pSelect->nEventId );
+        pSelect->nEventId = nullptr;
+        pSelect->Select();
+    }
+}
+
 sal_uInt16 PopupMenu::ImplExecute( vcl::Window* pW, const Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst )
 {
     if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) )
@@ -3092,6 +3110,7 @@ sal_uInt16 PopupMenu::ImplExecute( vcl::Window* pW, const Rectangle& rRect, Floa
         SalMenu* pMenu = ImplGetSalMenu();
         if( pMenu && bRealExecute && pMenu->ShowNativePopupMenu( pWin, aRect, nPopupModeFlags | FloatWinPopupFlags::GrabFocus ) )
         {
+            ImplFlushPendingSelect();
             pWin->StopExecute();
             pWin->doShutdown();
             pWindow->doLazyDelete();
@@ -3175,15 +3194,7 @@ sal_uInt16 PopupMenu::ImplExecute( vcl::Window* pW, const Rectangle& rRect, Floa
         pWindow->doLazyDelete();
         pWindow = nullptr;
 
-        // is there still Select?
-        Menu* pSelect = ImplFindSelectMenu();
-        if ( pSelect )
-        {
-            // Select should be called prior to leaving execute in a popup menu!
-            Application::RemoveUserEvent( pSelect->nEventId );
-            pSelect->nEventId = nullptr;
-            pSelect->Select();
-        }
+        ImplFlushPendingSelect();
     }
 
     return bRealExecute ? nSelectedId : 0;
@@ -3251,44 +3262,4 @@ ImplMenuDelData::~ImplMenuDelData()
         const_cast< Menu* >( mpMenu )->ImplRemoveDel( *this );
 }
 
-namespace vcl { namespace MenuInvalidator {
-
-struct MenuInvalidateListeners : public vcl::DeletionNotifier
-{
-    std::vector<Link<LinkParamNone*,void>>   m_aListeners;
-};
-
-static MenuInvalidateListeners* pMenuInvalidateListeners = nullptr;
-
-void AddMenuInvalidateListener(const Link<LinkParamNone*,void>& rLink)
-{
-    if(!pMenuInvalidateListeners)
-        pMenuInvalidateListeners = new MenuInvalidateListeners();
-    // ensure uniqueness
-    auto& rListeners = pMenuInvalidateListeners->m_aListeners;
-    if (std::find(rListeners.begin(), rListeners.end(), rLink) == rListeners.end())
-       rListeners.push_back( rLink );
-}
-
-void Invalidated()
-{
-    if(!pMenuInvalidateListeners)
-        return;
-
-    vcl::DeletionListener aDel( pMenuInvalidateListeners );
-
-    auto& rYieldListeners = pMenuInvalidateListeners->m_aListeners;
-    // Copy the list, because this can be destroyed when calling a Link...
-    std::vector<Link<LinkParamNone*,void>> aCopy( rYieldListeners );
-    for( Link<LinkParamNone*,void>& rLink : aCopy )
-    {
-        if (aDel.isDeleted()) break;
-        // check this hasn't been removed in some re-enterancy scenario fdo#47368
-        if( std::find(rYieldListeners.begin(), rYieldListeners.end(), rLink) != rYieldListeners.end() )
-            rLink.Call( nullptr );
-    }
-};
-
-} } // namespace vcl::MenuInvalidator
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menubarwindow.cxx b/vcl/source/window/menubarwindow.cxx
index 3209314..24b358e 100644
--- a/vcl/source/window/menubarwindow.cxx
+++ b/vcl/source/window/menubarwindow.cxx
@@ -215,6 +215,8 @@ void MenuBarWindow::ShowButtons( bool bClose, bool bFloat, bool bHide )
 {
     aCloseBtn->ShowItem(IID_DOCUMENTCLOSE, bClose);
     aCloseBtn->Show(bClose || !m_aAddButtons.empty());
+    if (pMenu->mpSalMenu)
+        pMenu->mpSalMenu->ShowCloseButton(bClose);
     aFloatBtn->Show( bFloat );
     aHideBtn->Show( bHide );
     Resize();
diff --git a/vcl/source/window/toolbox.cxx b/vcl/source/window/toolbox.cxx
index 140a473..30755ac 100644
--- a/vcl/source/window/toolbox.cxx
+++ b/vcl/source/window/toolbox.cxx
@@ -5019,20 +5019,19 @@ bool ToolBox::ImplActivateItem( vcl::KeyCode aKeyCode )
         else
         {
             mnDownItemId = mnCurItemId = mnHighItemId;
-            ImplToolItem* pItem = ImplGetItem( mnHighItemId );
-            if ( pItem->mnBits & ToolBoxItemBits::AUTOCHECK )
+            if (pToolItem && (pToolItem->mnBits & ToolBoxItemBits::AUTOCHECK))
             {
-                if ( pItem->mnBits & ToolBoxItemBits::RADIOCHECK )
+                if ( pToolItem->mnBits & ToolBoxItemBits::RADIOCHECK )
                 {
-                    if ( pItem->meState != TRISTATE_TRUE )
-                        SetItemState( pItem->mnId, TRISTATE_TRUE );
+                    if ( pToolItem->meState != TRISTATE_TRUE )
+                        SetItemState( pToolItem->mnId, TRISTATE_TRUE );
                 }
                 else
                 {
-                    if ( pItem->meState != TRISTATE_TRUE )
-                        pItem->meState = TRISTATE_TRUE;
+                    if ( pToolItem->meState != TRISTATE_TRUE )
+                        pToolItem->meState = TRISTATE_TRUE;
                     else
-                        pItem->meState = TRISTATE_FALSE;
+                        pToolItem->meState = TRISTATE_FALSE;
                 }
             }
             mnMouseModifier = aKeyCode.GetModifier();
diff --git a/vcl/source/window/toolbox2.cxx b/vcl/source/window/toolbox2.cxx
index c3d1173..2f355cb 100644
--- a/vcl/source/window/toolbox2.cxx
+++ b/vcl/source/window/toolbox2.cxx
@@ -1799,6 +1799,20 @@ bool ToolBox::ImplHasClippedItems()
     return false;
 }
 
+namespace
+{
+    MenuItemBits ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)
+    {
+        MenuItemBits nMenuItemBits = MenuItemBits::NONE;
+        if ((nToolItemBits & ToolBoxItemBits::CHECKABLE) ||
+            (nToolItemBits & ToolBoxItemBits::DROPDOWN))
+        {
+            nMenuItemBits |= MenuItemBits::CHECKABLE;
+        }
+        return nMenuItemBits;
+    }
+}
+
 void ToolBox::UpdateCustomMenu()
 {
     // fill clipped items into menu
@@ -1834,7 +1848,8 @@ void ToolBox::UpdateCustomMenu()
             if( it->IsClipped() )
             {
                 sal_uInt16 id = it->mnId + TOOLBOX_MENUITEM_START;
-                pMenu->InsertItem( id, it->maText, it->maImageOriginal, MenuItemBits::NONE, OString());
+                MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(it->mnBits);
+                pMenu->InsertItem( id, it->maText, it->maImageOriginal, nMenuItemBits, OString());
                 pMenu->SetItemCommand( id, it->maCommandStr );
                 pMenu->EnableItem( id, it->mbEnabled );
                 pMenu->CheckItem ( id, it->meState == TRISTATE_TRUE );
@@ -1851,7 +1866,8 @@ void ToolBox::UpdateCustomMenu()
             if( it->IsItemHidden() )
             {
                 sal_uInt16 id = it->mnId + TOOLBOX_MENUITEM_START;
-                pMenu->InsertItem( id, it->maText, it->maImageOriginal, MenuItemBits::NONE, OString() );
+                MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(it->mnBits);
+                pMenu->InsertItem( id, it->maText, it->maImageOriginal, nMenuItemBits, OString() );
                 pMenu->SetItemCommand( id, it->maCommandStr );
                 pMenu->EnableItem( id, it->mbEnabled );
                 pMenu->CheckItem( id, it->meState == TRISTATE_TRUE );
diff --git a/vcl/unx/gtk/a11y/atklistener.cxx b/vcl/unx/gtk/a11y/atklistener.cxx
index f9c25fd..479a516 100644
--- a/vcl/unx/gtk/a11y/atklistener.cxx
+++ b/vcl/unx/gtk/a11y/atklistener.cxx
@@ -143,7 +143,6 @@ void AtkListener::updateChildList(
                  m_aChildList.resize(std::min(nChildren2, n));
                  break;
              }
-             OSL_ASSERT(m_aChildList[n].is());
          }
      }
 }
diff --git a/vcl/unx/gtk/gloactiongroup.cxx b/vcl/unx/gtk/gloactiongroup.cxx
index 7d846eb..1728f13 100644
--- a/vcl/unx/gtk/gloactiongroup.cxx
+++ b/vcl/unx/gtk/gloactiongroup.cxx
@@ -100,8 +100,8 @@ g_lo_action_class_init (GLOActionClass *klass)
 
 struct _GLOActionGroupPrivate
 {
-    GHashTable  *table;  /* string -> GLOAction */
-    GtkSalFrame *frame;  /* Frame to which GActionGroup is associated. */
+    GHashTable  *table;    /* string -> GLOAction */
+    GtkSalFrame *frame;    /* Frame to which GActionGroup is associated. */
 };
 
 static void g_lo_action_group_iface_init (GActionGroupInterface *);
@@ -150,18 +150,7 @@ g_lo_action_group_query_action (GActionGroup        *group,
 {
     //SAL_INFO("vcl.unity", "g_lo_action_group_query_action on " << group);
     GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group);
-    GLOAction* action;
-
-    if (enabled)
-    {
-        GtkSalFrame* pFrame = lo_group->priv->frame;
-        if (pFrame) {
-            pFrame->EnsureDbusMenuSynced();
-        }
-    }
-
-    // note: EnsureDbusMenuSynced could have deleted the action!
-    action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name));
+    GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name));
 
     if (action == nullptr)
         return FALSE;
@@ -191,25 +180,13 @@ g_lo_action_group_perform_submenu_action (GLOActionGroup *group,
                                           const gchar    *action_name,
                                           GVariant       *state)
 {
+    gboolean bState = g_variant_get_boolean (state);
+    SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState);
 
-    GtkSalFrame* pFrame = group->priv->frame;
-    SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " for frame " << pFrame);
-
-    if (pFrame == nullptr)
-        return;
-
-    GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*> (pFrame->GetMenu());
-    SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " for menu " << pSalMenu);
-
-    if (pSalMenu != nullptr) {
-        gboolean bState = g_variant_get_boolean (state);
-        SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState);
-
-        if (bState)
-            pSalMenu->Activate();
-        else
-            pSalMenu->Deactivate (action_name);
-    }
+    if (bState)
+        GtkSalMenu::Activate(action_name);
+    else
+        GtkSalMenu::Deactivate(action_name);
 }
 
 static void
@@ -267,25 +244,9 @@ g_lo_action_group_activate (GActionGroup *group,
                             const gchar  *action_name,
                             GVariant     *parameter)
 {
-    GLOActionGroup *lo_group = G_LO_ACTION_GROUP (group);
-    GtkSalFrame *pFrame = lo_group->priv->frame;
-    SAL_INFO("vcl.unity", "g_lo_action_group_activate on group " << group << " for frame " << pFrame << " with parameter " << parameter);
-
-    if ( parameter != nullptr )
-        g_action_group_change_action_state( group, action_name, parameter );
-
-    if ( pFrame != nullptr )
-    {
-        GtkSalMenu* pSalMenu = static_cast< GtkSalMenu* >( pFrame->GetMenu() );
-        SAL_INFO("vcl.unity", "g_lo_action_group_activate for menu " << pSalMenu);
-
-        if ( pSalMenu != nullptr )
-        {
-            GLOAction* action = G_LO_ACTION (g_hash_table_lookup (lo_group->priv->table, action_name));
-            SAL_INFO("vcl.unity", "g_lo_action_group_activate dispatching action " << action << " named " << action_name << " on menu " << pSalMenu);
-            pSalMenu->DispatchCommand( action->item_id, action_name );
-        }
-    }
+    if (parameter != nullptr)
+        g_action_group_change_action_state(group, action_name, parameter);
+    GtkSalMenu::DispatchCommand(action_name);
 }
 
 void
@@ -315,7 +276,6 @@ g_lo_action_group_insert_stateful (GLOActionGroup     *group,
     {
         if (old_action != nullptr)
             g_lo_action_group_remove (group, action_name);
-//            g_action_group_action_removed (G_ACTION_GROUP (group), action_name);
 
         GLOAction* action = g_lo_action_new();
 
diff --git a/vcl/unx/gtk/glomenu.cxx b/vcl/unx/gtk/glomenu.cxx
index 6aa19e6..904065e 100644
--- a/vcl/unx/gtk/glomenu.cxx
+++ b/vcl/unx/gtk/glomenu.cxx
@@ -294,6 +294,7 @@ g_lo_menu_set_action_and_target_value (GLOMenu     *menu,
 
     g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ACTION, action_value);
     g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_TARGET, target_value);
+    g_lo_menu_set_attribute_value (menu, position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, nullptr);
 
     g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 1);
 }
diff --git a/vcl/unx/gtk/gtkdata.cxx b/vcl/unx/gtk/gtkdata.cxx
index 22ac431..71f9fea 100644
--- a/vcl/unx/gtk/gtkdata.cxx
+++ b/vcl/unx/gtk/gtkdata.cxx
@@ -110,6 +110,7 @@ GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay ) :
 #endif
 #endif
 
+    gtk_widget_set_default_direction(AllSettings::GetLayoutRTL() ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
 }
 
 GtkSalDisplay::~GtkSalDisplay()
@@ -1040,22 +1041,4 @@ void GtkSalDisplay::deregisterFrame( SalFrame* pFrame )
     SalGenericDisplay::deregisterFrame( pFrame );
 }
 
-#if GTK_CHECK_VERSION(3,0,0)
-void GtkSalDisplay::RefreshMenusUnity()
-{
-#ifdef ENABLE_GMENU_INTEGRATION
-    for(auto pSalFrame : m_aFrames) {
-        auto pGtkSalFrame( static_cast<GtkSalFrame*>(pSalFrame));
-        GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(pGtkSalFrame->GetMenu());
-        if(pSalMenu) {
-            pSalMenu->Activate();
-            pSalMenu->UpdateFull();
-        }
-    }
-#else
-    (void) this;
-#endif
-}
-#endif
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk/gtksalframe.cxx b/vcl/unx/gtk/gtksalframe.cxx
index 95fd581..076a60a 100644
--- a/vcl/unx/gtk/gtksalframe.cxx
+++ b/vcl/unx/gtk/gtksalframe.cxx
@@ -434,9 +434,6 @@ GtkSalFrame::GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
     m_bDefaultPos       = true;
     m_bDefaultSize      = ( (nStyle & SalFrameStyleFlags::SIZEABLE) && ! pParent );
     m_bWindowIsGtkPlug  = false;
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-    m_pLastSyncedDbusMenu = nullptr;
-#endif
     Init( pParent, nStyle );
 }
 
@@ -448,9 +445,6 @@ GtkSalFrame::GtkSalFrame( SystemParentData* pSysData )
     GetGenericData()->ErrorTrapPush();
     m_bDefaultPos       = true;
     m_bDefaultSize      = true;
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-    m_pLastSyncedDbusMenu = nullptr;
-#endif
     Init( pSysData );
 }
 
@@ -488,17 +482,6 @@ static void ObjectDestroyedNotify( gpointer data )
     }
 }
 
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-void GtkSalFrame::EnsureDbusMenuSynced()
-{
-    GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(GetMenu());
-    if(m_pLastSyncedDbusMenu != pSalMenu) {
-        m_pLastSyncedDbusMenu = pSalMenu;
-        static_cast<GtkSalMenu*>(pSalMenu)->Activate();
-    }
-}
-#endif
-
 static void hud_activated( gboolean hud_active, gpointer user_data )
 {
     if ( hud_active )
@@ -715,7 +698,7 @@ void on_registrar_available( GDBusConnection * /*connection*/,
     if ( pSalMenu != nullptr )
     {
         GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu);
-        pGtkSalMenu->Display( true );
+        pGtkSalMenu->EnableUnity(true);
         pGtkSalMenu->UpdateFull();
     }
 }
@@ -736,7 +719,7 @@ void on_registrar_unavailable( GDBusConnection * /*connection*/,
 
     if ( pSalMenu ) {
         GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu );
-        pGtkSalMenu->Display( false );
+        pGtkSalMenu->EnableUnity(false);
     }
 }
 #endif
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index b6295fa..00dd67b 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -16,6 +16,7 @@
 #include <unx/gtk/gtkdata.hxx>
 #include <unx/gtk/glomenu.h>
 #include <unx/gtk/gloactiongroup.h>
+#include <vcl/floatwin.hxx>
 #include <vcl/menu.hxx>
 #include <unx/gtk/gtkinst.hxx>
 
@@ -24,71 +25,25 @@
 #endif
 
 #include <sal/log.hxx>
+#include <window.h>
+#include <svids.hrc>
 
-// FIXME Copied from framework/inc/framework/menuconfiguration.hxx to
-// avoid circular dependency between modules. It should be in a common
-// header (probably in vcl).
-const sal_uInt16 START_ITEMID_WINDOWLIST    = 4600;
-const sal_uInt16 END_ITEMID_WINDOWLIST      = 4699;
-
-static bool bMenuVisibility = false;
+static bool bUnityMode = false;
 
 /*
- * This function generates the proper command name for all actions, including
- * duplicated or special ones.
+ * This function generates a unique command name for each menu item
  */
-static gchar* GetCommandForItem( GtkSalMenuItem* pSalMenuItem, gchar* aCurrentCommand, GActionGroup* pActionGroup )
+static gchar* GetCommandForItem(GtkSalMenuItem* pSalMenuItem)
 {
-    gchar* aCommand = nullptr;
-
-    sal_uInt16 nId = pSalMenuItem->mnId;
-    Menu* pMenu = pSalMenuItem->mpVCLMenu;
-
-    // If item belongs to window list, generate a command with "window-(id)" format.
-    if ( ( nId >= START_ITEMID_WINDOWLIST ) && ( nId <= END_ITEMID_WINDOWLIST ) )
-        aCommand = g_strdup_printf( "window-%d", nId );
-    else
-    {
-        if ( !pMenu )
-            return nullptr;
-
-        OUString aMenuCommand = pMenu->GetItemCommand( nId );
-        gchar* aCommandStr = g_strdup( OUStringToOString( aMenuCommand, RTL_TEXTENCODING_UTF8 ).getStr() );
-        aCommand = g_strdup( aCommandStr );
-
-        // Some items could have duplicated commands. A new one should be generated.
-        for ( sal_uInt16 i = 1; ; i++ )
-        {
-            if ( !g_action_group_has_action( pActionGroup, aCommand )
-                    || ( aCurrentCommand && g_strcmp0( aCurrentCommand, aCommand ) == 0 ) )
-                break;
-
-            g_free( aCommand );
-            aCommand = g_strdup_printf("dup:%d:%s", i, aCommandStr);
-        }
-
-        g_free( aCommandStr );
-    }
-
-    return aCommand;
+    OString aCommand("window-");
+    aCommand = aCommand + OString::number(reinterpret_cast<unsigned long>(pSalMenuItem->mpParentMenu));
+    aCommand = aCommand + "-" + OString::number(pSalMenuItem->mnId);
+    return g_strdup(aCommand.getStr());
 }
 
 bool GtkSalMenu::PrepUpdate()
 {
-    const GtkSalFrame* pFrame = GetFrame();
-    if (pFrame)
-    {
-        GtkSalFrame* pNonConstFrame = const_cast<GtkSalFrame*>(pFrame);
-        GtkSalMenu* pSalMenu = this;
-
-        if ( !pNonConstFrame->GetMenu() )
-            pNonConstFrame->SetMenu( pSalMenu );
-
-        if ( bMenuVisibility && mpMenuModel && mpActionGroup )
-            return true;
-    }
-
-    return false;
+    return mpMenuModel && mpActionGroup;
 }
 
 /*
@@ -112,14 +67,58 @@ void RemoveSpareItemsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, un
     }
 }
 
-void RemoveSpareSectionsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nLastSection )
+void RemoveDisabledItemsFromNativeMenu(GLOMenu* pMenu, GList** pOldCommandList,
+                                       sal_Int32 nSection, GActionGroup* pActionGroup)
+{
+    while (nSection >= 0)
+    {
+        sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection );
+        while (nSectionItems--)
+        {
+            gchar* pCommand = g_lo_menu_get_command_from_item_in_section(pMenu, nSection, nSectionItems);
+            // remove disabled entries
+            bool bRemove = g_action_group_get_action_enabled(pActionGroup, pCommand) == false;
+            if (!bRemove)
+            {
+                //also remove any empty submenus
+                GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nSectionItems);
+                if (pSubMenuModel)
+                {
+                    gint nSubMenuSections = g_menu_model_get_n_items(G_MENU_MODEL(pSubMenuModel));
+                    bRemove = (nSubMenuSections == 0 ||
+                              (nSubMenuSections == 1 && g_lo_menu_get_n_items_from_section(pSubMenuModel, 0) == 0));
+                }
+            }
+
+            if (bRemove)
+            {
+                //but tdf#86850 Always display clipboard functions
+                bRemove = g_strcmp0(pCommand, ".uno:Cut") &&
+                          g_strcmp0(pCommand, ".uno:Copy") &&
+                          g_strcmp0(pCommand, ".uno:Paste");
+            }
+
+            if (bRemove)
+            {
+                if (pCommand != nullptr && pOldCommandList != nullptr)
+                    *pOldCommandList = g_list_append(*pOldCommandList, g_strdup(pCommand));
+                g_lo_menu_remove_from_section(pMenu, nSection, nSectionItems);
+            }
+
+            g_free(pCommand);
+        }
+        --nSection;
+    }
+}
+
+void RemoveSpareSectionsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, sal_Int32 nLastSection )
 {
     if ( pMenu == nullptr || pOldCommandList == nullptr )
         return;
 
     sal_Int32 n = g_menu_model_get_n_items( G_MENU_MODEL( pMenu ) ) - 1;
 
-    for ( ; n > (sal_Int32) nLastSection; n-- )
+    for ( ; n > nLastSection; n--)
     {
         RemoveSpareItemsFromNativeMenu( pMenu, pOldCommandList, n, 0 );
         g_lo_menu_remove( pMenu, n );
@@ -171,7 +170,7 @@ void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList,
     }
 }
 
-void GtkSalMenu::ImplUpdate( gboolean bRecurse )
+void GtkSalMenu::ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries)
 {
     SolarMutexGuard aGuard;
 
@@ -179,6 +178,13 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
     if( !PrepUpdate() )
         return;
 
+    if (mbNeedsUpdate)
+    {
+        mbNeedsUpdate = false;
+        if (mbMenuBar)
+            maUpdateMenuBarIdle.Stop();
+    }
+
     Menu* pVCLMenu = mpVCLMenu;
     GLOMenu* pLOMenu = G_LO_MENU( mpMenuModel );
     GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
@@ -238,7 +244,7 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
             pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand );
 
         // Get the new command for the item.
-        gchar* aNativeCommand = GetCommandForItem( pSalMenuItem, aCurrentCommand, mpActionGroup );
+        gchar* aNativeCommand = GetCommandForItem(pSalMenuItem);
 
         // Force updating of native menu labels.
         NativeSetItemText( nSection, nItemPos, aText );
@@ -257,7 +263,7 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
 
         if ( pSubmenu && pSubmenu->GetMenu() )
         {
-            NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, FALSE, TRUE );
+            bool bNonMenuChangedToMenu = NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, FALSE, TRUE );
             pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
 
             GLOMenu* pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
@@ -270,12 +276,12 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
 
             g_object_unref( pSubMenuModel );
 
-            if ( bRecurse )
+            if (bRecurse || bNonMenuChangedToMenu)
             {
                 SAL_INFO("vcl.unity", "preparing submenu  " << pSubMenuModel << " to menu model " << G_MENU_MODEL(pSubMenuModel) << " and action group " << G_ACTION_GROUP(pActionGroup));
                 pSubmenu->SetMenuModel( G_MENU_MODEL( pSubMenuModel ) );
                 pSubmenu->SetActionGroup( G_ACTION_GROUP( pActionGroup ) );
-                pSubmenu->ImplUpdate( bRecurse );
+                pSubmenu->ImplUpdate(true, bRemoveDisabledEntries);
             }
         }
 
@@ -285,6 +291,12 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
         ++validItems;
     }
 
+    if (bRemoveDisabledEntries)
+    {
+        // Delete disabled items in last section.
+        RemoveDisabledItemsFromNativeMenu(pLOMenu, &pOldCommandList, nSection, G_ACTION_GROUP(pActionGroup));
+    }
+
     // Delete extra items in last section.
     RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems );
 
@@ -297,12 +309,101 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
 
 void GtkSalMenu::Update()
 {
-    ImplUpdate( FALSE );
+    //find out if top level is a menubar or not, if not, then its a popup menu
+    //hierarchy and in those we hide (most) disabled entries
+    const GtkSalMenu* pMenu = this;
+    while (pMenu->mpParentSalMenu)
+        pMenu = pMenu->mpParentSalMenu;
+    ImplUpdate(false, !pMenu->mbMenuBar);
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+static void MenuPositionFunc(GtkMenu* menu, gint* x, gint* y, gboolean* push_in, gpointer user_data)
+{
+    Point *pPos = static_cast<Point*>(user_data);
+    *x = pPos->X();
+    if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL)
+    {
+        GtkRequisition natural_size;
+        gtk_widget_get_preferred_size(GTK_WIDGET(menu), nullptr, &natural_size);
+        *x -= natural_size.width;
+    }
+    *y = pPos->Y();
+    *push_in = false;
 }
+#endif
 
-void GtkSalMenu::UpdateFull()
+bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const Rectangle& rRect,
+                                     FloatWinPopupFlags nFlags)
 {
-    ImplUpdate( TRUE );
+#if GTK_CHECK_VERSION(3,0,0)
+    guint nButton;
+    guint32 nTime;
+
+    //typically there is an event, and we can then distinguish if this was
+    //launched from the keyboard (gets auto-mnemoniced) or the mouse (which
+    //doesn't)
+    GdkEvent *pEvent = gtk_get_current_event();
+    if (pEvent)
+    {
+        gdk_event_get_button(pEvent, &nButton);
+        nTime = gdk_event_get_time(pEvent);
+    }
+    else
+    {
+        nButton = 0;
+        nTime = gtk_get_current_event_time();
+    }
+
+    VclPtr<vcl::Window> xParent = pWin->ImplGetWindowImpl()->mpRealParent;
+    mpFrame = static_cast<GtkSalFrame*>(xParent->ImplGetFrame());
+
+    // do the same strange semantics as vcl popup windows to arrive at a frame geometry
+    // in mirrored UI case; best done by actually executing the same code
+    sal_uInt16 nArrangeIndex;
+    Point aPos = FloatingWindow::ImplCalcPos(pWin, rRect, nFlags, nArrangeIndex);
+    aPos = FloatingWindow::ImplConvertToAbsPos(xParent, aPos);
+
+    GLOActionGroup* pActionGroup = g_lo_action_group_new(static_cast<gpointer>(mpFrame));
+    mpActionGroup = G_ACTION_GROUP(pActionGroup);
+    mpMenuModel = G_MENU_MODEL(g_lo_menu_new());
+    // Generate the main menu structure, populates mpMenuModel
+    UpdateFull();
+
+    GtkWidget *pWidget = gtk_menu_new_from_model(mpMenuModel);
+    gtk_menu_attach_to_widget(GTK_MENU(pWidget), mpFrame->getMouseEventWidget(), nullptr);
+
+    gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", mpActionGroup);
+
+    //run in a sub main loop because we need to keep vcl PopupMenu alive to use
+    //it during DispatchCommand, returning now to the outer loop causes the
+    //launching PopupMenu to be destroyed, instead run the subloop here
+    //until the gtk menu is destroyed
+    GMainLoop* pLoop = g_main_loop_new(nullptr, true);
+    g_signal_connect_swapped(G_OBJECT(pWidget), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop);
+    gtk_menu_popup(GTK_MENU(pWidget), nullptr, nullptr, MenuPositionFunc,
+                   &aPos, nButton, nTime);
+    if (g_main_loop_is_running(pLoop))
+    {
+        gdk_threads_leave();
+        g_main_loop_run(pLoop);
+        gdk_threads_enter();
+    }
+    g_main_loop_unref(pLoop);
+
+    gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", nullptr);
+
+    gtk_widget_destroy(pWidget);
+
+    g_object_unref(mpActionGroup);
+
+    return true;
+#else
+    (void)pWin;
+    (void)rRect;
+    (void)nFlags;
+    return false;
+#endif
 }
 
 /*
@@ -311,33 +412,68 @@ void GtkSalMenu::UpdateFull()
 
 GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
     mbMenuBar( bMenuBar ),
+    mbUnityMode ( false ),
+    mbNeedsUpdate( false ),
+    mpMenuBarWidget( nullptr ),
+    mpCloseButton( nullptr ),
     mpVCLMenu( nullptr ),
     mpParentSalMenu( nullptr ),
     mpFrame( nullptr ),
     mpMenuModel( nullptr ),
     mpActionGroup( nullptr )
 {
+    //typically this only gets called after the menu has been customized on the
+    //next idle slot, in the normal case of a new menubar SetFrame is called
+    //directly long before this idle would get called.
+    maUpdateMenuBarIdle.SetPriority(SchedulerPriority::HIGHEST);
+    maUpdateMenuBarIdle.SetIdleHdl(LINK(this, GtkSalMenu, MenuBarHierarchyChangeHandler));
+    maUpdateMenuBarIdle.SetDebugName("Native Gtk Menu Update Idle");
 }
 
-GtkSalMenu::~GtkSalMenu()
+IMPL_LINK_NOARG_TYPED(GtkSalMenu, MenuBarHierarchyChangeHandler, Idle *, void)
 {
-    SolarMutexGuard aGuard;
+    SAL_WARN_IF(!mpFrame, "vcl.gtk", "MenuBar layout changed, but no frame for some reason!");
+    if (!mpFrame)
+        return;
+    SetFrame(mpFrame);
+}
 
-    if ( mbMenuBar )
+void GtkSalMenu::SetNeedsUpdate()
+{
+    GtkSalMenu* pMenu = this;
+    while (pMenu && !pMenu->mbNeedsUpdate)
     {
-        if ( mpMenuModel )
-        {
-//            g_lo_menu_remove( G_LO_MENU( mpMenuModel ), 0 );
-            g_object_unref( mpMenuModel );
-        }
+        pMenu->mbNeedsUpdate = true;
+        if (mbMenuBar)
+            maUpdateMenuBarIdle.Start();
+        pMenu = pMenu->mpParentSalMenu;
     }
+}
+
+void GtkSalMenu::SetMenuModel(GMenuModel* pMenuModel)
+{
+    if (mpMenuModel)
+        g_object_unref(mpMenuModel);
+    mpMenuModel = pMenuModel;
+    if (mpMenuModel)
+        g_object_ref(mpMenuModel);
+}
+
+GtkSalMenu::~GtkSalMenu()
+{
+    SolarMutexGuard aGuard;
+
+    DestroyMenuBarWidget();
+
+    if (mpMenuModel)
+        g_object_unref(mpMenuModel);
 
     maItems.clear();
 }
 
 bool GtkSalMenu::VisibleMenuBar()
 {
-    return bMenuVisibility;
+    return mbMenuBar;
 }
 
 void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
@@ -351,12 +487,15 @@ void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
         maItems.insert( maItems.begin() + nPos, pItem );
 
     pItem->mpParentMenu = this;
+
+    SetNeedsUpdate();
 }
 
 void GtkSalMenu::RemoveItem( unsigned nPos )
 {
     SolarMutexGuard aGuard;
     maItems.erase( maItems.begin() + nPos );
+    SetNeedsUpdate();
 }
 
 void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned )
@@ -370,69 +509,135 @@ void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsig
 
     pGtkSubMenu->mpParentSalMenu = this;
     pItem->mpSubMenu = pGtkSubMenu;
+
+    SetNeedsUpdate();
 }
 
-static bool bInvalidMenus = false;
-static gboolean RefreshMenusUnity(gpointer)
+#if GTK_CHECK_VERSION(3,0,0)
+static void CloseMenuBar(GtkWidget *, gpointer pMenu)
+{
+    Application::PostUserEvent(static_cast<MenuBar*>(pMenu)->GetCloseButtonClickHdl());
+}
+#endif
+
+void GtkSalMenu::ShowCloseButton(bool bShow)
 {
-    SolarMutexGuard g;
 #if GTK_CHECK_VERSION(3,0,0)
-    GetGtkSalData()->GetGtkDisplay()->RefreshMenusUnity();
-#else
-    SalDisplay* pSalDisplay = vcl_sal::getSalDisplay(GetGenericData());
-    std::list< SalFrame* >::const_iterator pSalFrame = pSalDisplay->getFrames().begin();
-    std::list< SalFrame* >::const_iterator pEndSalFrame = pSalDisplay->getFrames().end();
-    for(; pSalFrame != pEndSalFrame; ++pSalFrame) {
-        const GtkSalFrame* pGtkSalFrame = static_cast< const GtkSalFrame* >( *pSalFrame );
-        GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(pGtkSalFrame);
-        GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(pFrameNonConst->GetMenu());
-        if(pSalMenu) {
-            pSalMenu->Activate();
-            pSalMenu->UpdateFull();
-        }
+    assert(mbMenuBar);
+    MenuBar *pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu);
+    if (!bShow)
+    {
+        if (mpCloseButton)
+            gtk_widget_destroy(mpCloseButton);
+        return;
     }
+
+    mpCloseButton = gtk_button_new();
+    g_signal_connect(mpCloseButton, "clicked", G_CALLBACK(CloseMenuBar), pVclMenuBar);
+
+    gtk_button_set_relief(GTK_BUTTON(mpCloseButton), GTK_RELIEF_NONE);
+    gtk_button_set_focus_on_click(GTK_BUTTON(mpCloseButton), false);
+    gtk_widget_set_can_focus(mpCloseButton, false);
+
+    GtkStyleContext *pButtonContext = gtk_widget_get_style_context(GTK_WIDGET(mpCloseButton));
+
+    GtkCssProvider *pProvider = gtk_css_provider_new();
+    const gchar data[] = "* { "
+      "padding: 0;"
+      "margin-left: 8px;"
+      "margin-right: 8px;"
+      "min-width: 18px;"
+      "min-height: 18px;"
+      "}";
+    gtk_css_provider_load_from_data(pProvider, data, -1, nullptr);
+    gtk_style_context_add_provider(pButtonContext,
+                                   GTK_STYLE_PROVIDER(pProvider),
+                                   GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+    gtk_style_context_add_class(pButtonContext, "flat");
+    gtk_style_context_add_class(pButtonContext, "small-button");
+
+    GIcon* icon = g_themed_icon_new_with_default_fallbacks("window-close-symbolic");
+    GtkWidget* image = gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU);
+    gtk_widget_show(image);
+    g_object_unref(icon);
+
+    OUString sToolTip(VclResId(SV_HELPTEXT_CLOSEDOCUMENT));
+    gtk_widget_set_tooltip_text(mpCloseButton,
+        OUStringToOString(sToolTip, RTL_TEXTENCODING_UTF8).getStr());
+
+    gtk_widget_set_valign(mpCloseButton, GTK_ALIGN_CENTER);
+
+    gtk_container_add(GTK_CONTAINER(mpCloseButton), image);
+    gtk_grid_attach(GTK_GRID(mpMenuBarWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1);
+    gtk_widget_show_all(mpCloseButton);
+#else
+    (void)bShow;
 #endif
-    bInvalidMenus = false;
-    return FALSE;
 }
 
-static void RefreshMenusUnity(void*, LinkParamNone*)
+#if GTK_CHECK_VERSION(3,0,0)
+//hack-around https://bugzilla.gnome.org/show_bug.cgi?id=762756
+static void ReturnFocus(GtkMenuShell *, gpointer pWidget)
 {
-    if(!bInvalidMenus) {
-        g_timeout_add(10, &RefreshMenusUnity, nullptr);
-        bInvalidMenus = true;
-    }
+    GtkWidget* pTopLevel = static_cast<GtkWidget*>(pWidget);
+    GdkWindow *window = gtk_widget_get_window(pTopLevel);
+    GdkEvent *fevent = gdk_event_new(GDK_FOCUS_CHANGE);
+
+    fevent->focus_change.type = GDK_FOCUS_CHANGE;
+    fevent->focus_change.window = GDK_WINDOW(g_object_ref(window));
+    fevent->focus_change.in = TRUE;
+    gtk_widget_send_focus_change(pTopLevel, fevent);
+    gdk_event_free(fevent);
 }
+#endif
 
-static Link<LinkParamNone*,void>* getRefreshLinkInstance()
+void GtkSalMenu::CreateMenuBarWidget()
 {
-    static Link<LinkParamNone*,void>* pLink = nullptr;
-    if(!pLink) {
-        pLink = new Link<LinkParamNone*,void>(nullptr, &RefreshMenusUnity);
-    }
-    return pLink;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkGrid* pGrid = mpFrame->getTopLevelGridWidget();
+    mpMenuBarWidget = gtk_grid_new();
+
+    gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true);
+    gtk_grid_insert_row(pGrid, 0);
+    gtk_grid_attach(pGrid, mpMenuBarWidget, 0, 0, 1, 1);
+
+    GtkWidget *pMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel);
+    gtk_widget_insert_action_group(pMenuBarWidget, "win", mpActionGroup);
+    gtk_widget_set_hexpand(GTK_WIDGET(pMenuBarWidget), true);
+    gtk_grid_attach(GTK_GRID(mpMenuBarWidget), pMenuBarWidget, 0, 0, 1, 1);
+    g_signal_connect(G_OBJECT(pMenuBarWidget), "deactivate", G_CALLBACK(ReturnFocus), mpFrame->getWindow());
+
+    gtk_widget_show_all(mpMenuBarWidget);
+#endif
 }
 
-void GtkSalMenu::SetFrame( const SalFrame* pFrame )
+void GtkSalMenu::DestroyMenuBarWidget()
 {
-    SolarMutexGuard aGuard;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (mpMenuBarWidget)
     {
-        vcl::MenuInvalidator::AddMenuInvalidateListener(*getRefreshLinkInstance());
+        gtk_widget_destroy(mpMenuBarWidget);
+        mpMenuBarWidget = nullptr;
     }
+#endif
+}
 
+void GtkSalMenu::SetFrame(const SalFrame* pFrame)
+{
+    SolarMutexGuard aGuard;
     assert(mbMenuBar);
     SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
-    mpFrame = static_cast< const GtkSalFrame* >( pFrame );
-    GtkSalFrame* pFrameNonConst = const_cast<GtkSalFrame*>(mpFrame);
+    mpFrame = const_cast<GtkSalFrame*>(static_cast<const GtkSalFrame*>(pFrame));
 
     // if we had a menu on the GtkSalMenu we have to free it as we generate a
     // full menu anyway and we might need to reuse an existing model and
     // actiongroup
-    pFrameNonConst->SetMenu( this );
-    pFrameNonConst->EnsureAppMenuWatch();
+    mpFrame->SetMenu( this );
+    mpFrame->EnsureAppMenuWatch();
 
     // Clean menu model and action group if needed.
-    GtkWidget* pWidget = pFrameNonConst->getWindow();
+    GtkWidget* pWidget = mpFrame->getWindow();
     GdkWindow* gdkWindow = gtk_widget_get_window( pWidget );
 
     GLOMenu* pMenuModel = G_LO_MENU( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) );
@@ -454,10 +659,17 @@ void GtkSalMenu::SetFrame( const SalFrame* pFrame )
     }
 
     // Generate the main menu structure.
-    if (bMenuVisibility)
-        UpdateFull();
+    UpdateFull();
 
     g_lo_menu_insert_section( pMenuModel, 0, nullptr, mpMenuModel );
+
+#if GTK_CHECK_VERSION(3,0,0)
+    if (!mbUnityMode)
+    {
+        DestroyMenuBarWidget();
+        CreateMenuBarWidget();
+    }
+#endif
 }
 
 const GtkSalFrame* GtkSalMenu::GetFrame() const
@@ -562,7 +774,7 @@ void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, con
     g_free( aCurrentAccel );
 }
 
-void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
+bool GtkSalMenu::NativeSetItemCommand( unsigned nSection,
                                        unsigned nItemPos,
                                        sal_uInt16 nId,
                                        const gchar* aCommand,
@@ -570,35 +782,38 @@ void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
                                        gboolean bChecked,
                                        gboolean bIsSubmenu )
 {
+    bool bSubMenuAddedOrRemoved = false;
+
     SolarMutexGuard aGuard;
     GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
 
     GVariant *pTarget = nullptr;
 
-    if ( g_action_group_has_action( mpActionGroup, aCommand ) == FALSE ) {
-        if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu )
-        {
-            // Item is a checkmark button.
-            GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_BOOLEAN) );
-            GVariant* pState = g_variant_new_boolean( bChecked );
+    if (g_action_group_has_action(mpActionGroup, aCommand))
+        g_lo_action_group_remove(pActionGroup, aCommand);
 
-            g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, nullptr, pStateType, nullptr, pState );
-        }
-        else if ( nBits & MenuItemBits::RADIOCHECK )
-        {
-            // Item is a radio button.
-            GVariantType* pParameterType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
-            GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
-            GVariant* pState = g_variant_new_string( "" );
-            pTarget = g_variant_new_string( aCommand );
+    if ( ( nBits & MenuItemBits::CHECKABLE ) || bIsSubmenu )
+    {
+        // Item is a checkmark button.
+        GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_BOOLEAN) );
+        GVariant* pState = g_variant_new_boolean( bChecked );
 
-            g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, nullptr, pState );
-        }
-        else
-        {
-            // Item is not special, so insert a stateless action.
-            g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE );
-        }
+        g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, bIsSubmenu, nullptr, pStateType, nullptr, pState );
+    }
+    else if ( nBits & MenuItemBits::RADIOCHECK )
+    {
+        // Item is a radio button.
+        GVariantType* pParameterType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
+        GVariantType* pStateType = g_variant_type_new( reinterpret_cast<gchar const *>(G_VARIANT_TYPE_STRING) );
+        GVariant* pState = g_variant_new_string( "" );
+        pTarget = g_variant_new_string( aCommand );
+
+        g_lo_action_group_insert_stateful( pActionGroup, aCommand, nId, FALSE, pParameterType, pStateType, nullptr, pState );
+    }
+    else
+    {
+        // Item is not special, so insert a stateless action.
+        g_lo_action_group_insert( pActionGroup, aCommand, nId, FALSE );
     }
 
     GLOMenu* pMenu = G_LO_MENU( mpMenuModel );
@@ -608,6 +823,18 @@ void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
 
     if ( aCurrentCommand == nullptr || g_strcmp0( aCurrentCommand, aCommand ) != 0 )
     {
+        bool bOldHasSubmenu = g_lo_menu_get_submenu_from_item_in_section(pMenu, nSection, nItemPos) != nullptr;
+        bSubMenuAddedOrRemoved = bOldHasSubmenu != bIsSubmenu;
+        if (bSubMenuAddedOrRemoved)
+        {
+            //tdf#98636 its not good enough to unset the "submenu-action" attribute to change something
+            //from a submenu to a non-submenu item, so remove the old one entirely and re-add it to
+            //support achieving that
+            gchar* pLabel = g_lo_menu_get_label_from_item_in_section(pMenu, nSection, nItemPos);
+            g_lo_menu_remove_from_section(pMenu, nSection, nItemPos);
+            g_lo_menu_insert_in_section(pMenu, nSection, nItemPos, pLabel);
+        }
+
         g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand );
 
         gchar* aItemCommand = g_strconcat("win.", aCommand, NULL );
@@ -628,128 +855,89 @@ void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
 
     if (pTarget)
         g_variant_unref(pTarget);
+
+    return bSubMenuAddedOrRemoved;
 }
 
-GtkSalMenu* GtkSalMenu::GetMenuForItemCommand(gchar* aCommand, int& rDupsToSkip, gboolean bGetSubmenu)
+GtkSalMenu* GtkSalMenu::GetTopLevel()
 {
-    SolarMutexGuard aGuard;
-    GtkSalMenu* pMenu = nullptr;
-    for ( size_t nPos = 0; nPos < maItems.size(); nPos++ )
-    {
-        GtkSalMenuItem *pSalItem = maItems[ nPos ];
-
-        OUString aItemCommand = mpVCLMenu->GetItemCommand( pSalItem->mnId );
-        // Do not join the following two lines, or the OString will be destroyed
-        // immediately, and the gchar* pointed to by aItemCommandStr will be
-        // freed before it can be used - fdo#69090
-        OString aItemCommandOStr = OUStringToOString( aItemCommand, RTL_TEXTENCODING_UTF8 );
-        gchar* aItemCommandStr = const_cast<gchar*>(aItemCommandOStr.getStr());
-
-        bool bFound = g_strcmp0( aItemCommandStr, aCommand ) == 0;
-        if (bFound && rDupsToSkip)
-        {
-            --rDupsToSkip;
-            bFound = false;
-        }
-        if (bFound)
-        {
-            pMenu = bGetSubmenu ? pSalItem->mpSubMenu : this;
-            break;
-        }
-        else
-        {
-            if ( pSalItem->mpSubMenu != nullptr )
-                pMenu = pSalItem->mpSubMenu->GetMenuForItemCommand(aCommand, rDupsToSkip, bGetSubmenu);
-
-            if ( pMenu != nullptr )
-               break;
-        }
-    }
-
+    GtkSalMenu *pMenu = this;
+    while (pMenu->mpParentSalMenu)
+        pMenu = pMenu->mpParentSalMenu;
     return pMenu;
 }
 
+typedef std::pair<GtkSalMenu*, sal_uInt16> MenuAndId;
+
 namespace
 {
-    const gchar* DetermineDupIndex(const gchar *aCommand, int& rDupsToSkip)
+    MenuAndId decode_command(const gchar *action_name)
     {
-        if (g_str_has_prefix(aCommand, "dup:"))
-        {
-            aCommand = aCommand + strlen("dup:");
-            gchar *endptr;
-            rDupsToSkip = g_ascii_strtoll(aCommand, &endptr, 10);
-            aCommand = endptr+1;
-        }
-        else
-            rDupsToSkip = 0;
+        OString sCommand(action_name);
+
+        sal_Int32 nIndex = 0;
+        OString sWindow = sCommand.getToken(0, '-', nIndex);
+        OString sGtkSalMenu = sCommand.getToken(0, '-', nIndex);
+        OString sItemId = sCommand.getToken(0, '-', nIndex);
+
+        GtkSalMenu* pSalSubMenu = reinterpret_cast<GtkSalMenu*>(sGtkSalMenu.toInt64());
+
+        assert(sWindow == "window" && pSalSubMenu);
+        (void)sWindow;
 
-        return aCommand;
+        return MenuAndId(pSalSubMenu, sItemId.toInt32());
     }
 }
 
-void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
+void GtkSalMenu::DispatchCommand(const gchar *pCommand)
 {
     SolarMutexGuard aGuard;
-    // Only the menubar is allowed to dispatch commands.
-    if ( !mbMenuBar )
-        return;
-
-    int nDupsToSkip;
-    aCommand = DetermineDupIndex(aCommand, nDupsToSkip);
-    GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aCommand), nDupsToSkip, FALSE );
-    Menu* pSubMenu = ( pSalSubMenu != nullptr ) ? pSalSubMenu->GetMenu() : nullptr;
-
-    MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
-    pMenuBar->HandleMenuCommandEvent( pSubMenu, itemId );
+    MenuAndId aMenuAndId = decode_command(pCommand);
+    GtkSalMenu* pSalSubMenu = aMenuAndId.first;
+    GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel();
+    pTopLevel->GetMenu()->HandleMenuCommandEvent(pSalSubMenu->GetMenu(), aMenuAndId.second);
 }
 
-void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
+void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar)
 {
-    pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
-    for ( size_t nPos = 0; nPos < maItems.size(); nPos++ )
+    for (GtkSalMenuItem* pSalItem : maItems)
     {
-        GtkSalMenuItem *pSalItem = maItems[ nPos ];
         if ( pSalItem->mpSubMenu != nullptr )
         {
+            pMenuBar->HandleMenuActivateEvent(pSalItem->mpSubMenu->GetMenu());
             pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar);
             pSalItem->mpSubMenu->Update();
+            pMenuBar->HandleMenuDeActivateEvent(pSalItem->mpSubMenu->GetMenu());
         }
     }
 }
 
-void GtkSalMenu::Activate()
+void GtkSalMenu::Activate(const gchar* pCommand)
 {
-    if ( !mbMenuBar )
-        return;
-    ActivateAllSubmenus(static_cast<MenuBar*>(mpVCLMenu));
+    MenuAndId aMenuAndId = decode_command(pCommand);
+    GtkSalMenu* pSalSubMenu = aMenuAndId.first;
+    GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel();
+    pTopLevel->GetMenu()->HandleMenuActivateEvent(pSalSubMenu->GetMenu());
 }
 
-void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
+void GtkSalMenu::Deactivate(const gchar* pCommand)
 {
-    if ( !mbMenuBar )
-        return;
-
-    int nDupsToSkip;
-    aMenuCommand = DetermineDupIndex(aMenuCommand, nDupsToSkip);
-    GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), nDupsToSkip, TRUE );
-
-    if ( pSalSubMenu != nullptr ) {
-        MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
-        pMenuBar->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
-    }
+    MenuAndId aMenuAndId = decode_command(pCommand);
+    GtkSalMenu* pSalSubMenu = aMenuAndId.first;
+    GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel();
+    pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pSalSubMenu->GetMenu());
 }
 
-void GtkSalMenu::Display( bool bVisible )
+void GtkSalMenu::EnableUnity(bool bEnable)
 {
-    if ( !mbMenuBar || mpVCLMenu == nullptr )
-        return;
-
-    bMenuVisibility = bVisible;
-
-    bool bVCLMenuVisible = !bVisible;
-
-    MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
-    pMenuBar->SetDisplayable( bVCLMenuVisible );
+    if (bUnityMode != bEnable)
+    {
+        if (!bEnable)
+            CreateMenuBarWidget();
+        else
+            DestroyMenuBarWidget();
+        bUnityMode = bEnable;
+    }
 }
 
 bool GtkSalMenu::IsItemVisible( unsigned nPos )
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
index 024735f..2d2df88 100644
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
@@ -481,9 +481,6 @@ GtkSalFrame::GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
     m_bDefaultPos       = true;
     m_bDefaultSize      = ( (nStyle & SalFrameStyleFlags::SIZEABLE) && ! pParent );
     m_bWindowIsGtkPlug  = false;
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-    m_pLastSyncedDbusMenu = nullptr;
-#endif
     Init( pParent, nStyle );
 }
 
@@ -497,9 +494,6 @@ GtkSalFrame::GtkSalFrame( SystemParentData* pSysData )
     GetGenericData()->ErrorTrapPush();
     m_bDefaultPos       = true;
     m_bDefaultSize      = true;
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-    m_pLastSyncedDbusMenu = nullptr;
-#endif
     Init( pSysData );
 }
 
@@ -514,17 +508,6 @@ static void ObjectDestroyedNotify( gpointer data )
     }
 }
 
-#if defined(ENABLE_DBUS) && ENABLE_GIO
-void GtkSalFrame::EnsureDbusMenuSynced()
-{
-    GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(GetMenu());
-    if(m_pLastSyncedDbusMenu != pSalMenu) {
-        m_pLastSyncedDbusMenu = pSalMenu;
-        static_cast<GtkSalMenu*>(pSalMenu)->Activate();
-    }
-}
-#endif
-
 static void hud_activated( gboolean hud_active, gpointer user_data )
 {
     if ( hud_active )
@@ -751,7 +734,7 @@ void on_registrar_available( GDBusConnection * /*connection*/,
     if ( pSalMenu != nullptr )
     {
         GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu);
-        pGtkSalMenu->Display( true );
+        pGtkSalMenu->EnableUnity(true);
         pGtkSalMenu->UpdateFull();
     }
 }
@@ -772,7 +755,7 @@ void on_registrar_unavailable( GDBusConnection * /*connection*/,
 
     if ( pSalMenu ) {
         GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu );
-        pGtkSalMenu->Display( false );
+        pGtkSalMenu->EnableUnity(false);
     }
 }
 #endif
@@ -850,6 +833,8 @@ GtkSalFrame::~GtkSalFrame()
         gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) );
     if( m_pEventBox )
         gtk_widget_destroy( GTK_WIDGET(m_pEventBox) );
+    if( m_pTopLevelGrid )
+        gtk_widget_destroy( GTK_WIDGET(m_pTopLevelGrid) );
     {
         SolarMutexGuard aGuard;
 #if defined ENABLE_GMENU_INTEGRATION
@@ -995,10 +980,15 @@ GtkWidget *GtkSalFrame::getMouseEventWidget() const
 
 void GtkSalFrame::InitCommon()
 {
+    m_pTopLevelGrid = GTK_GRID(gtk_grid_new());
+    gtk_container_add(GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pTopLevelGrid));
+
     m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new());
     gtk_widget_add_events( GTK_WIDGET(m_pEventBox),
                            GDK_ALL_EVENTS_MASK );
-    gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pEventBox) );
+    gtk_widget_set_vexpand(GTK_WIDGET(m_pEventBox), true);
+    gtk_widget_set_hexpand(GTK_WIDGET(m_pEventBox), true);
+    gtk_grid_attach(m_pTopLevelGrid, GTK_WIDGET(m_pEventBox), 0, 0, 1, 1);
 
     // add the fixed container child,
     // fixed is needed since we have to position plugin windows
@@ -1101,7 +1091,7 @@ void GtkSalFrame::InitCommon()
                            );
 
     // show the widgets
-    gtk_widget_show_all( GTK_WIDGET(m_pEventBox) );
+    gtk_widget_show_all(GTK_WIDGET(m_pTopLevelGrid));
 
     // realize the window, we need an XWindow id
     gtk_widget_realize( m_pWindow );
@@ -3017,7 +3007,7 @@ gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame
     if( !aDel.isDeleted() && pThis->m_pIMHandler )
         pThis->m_pIMHandler->updateIMSpotLocation();
 
-    return true;
+    return false;
 }
 
 gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame )
@@ -3393,6 +3383,7 @@ void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame )
     {
         pThis->m_pFixedContainer = nullptr;
         pThis->m_pEventBox = nullptr;
+        pThis->m_pTopLevelGrid = nullptr;
         pThis->m_pWindow = nullptr;
         pThis->InvalidateGraphics();
     }
diff --git a/vcl/unx/gtk3/gtk3salnativewidgets-gtk.cxx b/vcl/unx/gtk3/gtk3salnativewidgets-gtk.cxx
index 14aba8a..08bfc0e 100644
--- a/vcl/unx/gtk3/gtk3salnativewidgets-gtk.cxx
+++ b/vcl/unx/gtk3/gtk3salnativewidgets-gtk.cxx
@@ -2323,7 +2323,6 @@ GtkSalGraphics::GtkSalGraphics( GtkSalFrame *pFrame, GtkWidget *pWindow )
         return;
 
     style_loaded = true;
-    gtk_init(nullptr, nullptr);
     /* Load the GtkStyleContexts, it might be a bit slow, but usually,
      * gtk apps create a lot of widgets at startup, so, it shouldn't be
      * too slow */
-- 
2.7.3