native gtk3 popup menus

f41
Caolán McNamara 9 years ago
parent 4b799c8e78
commit 300695674c

@ -0,0 +1,328 @@
From 9e64526a0671b52cdb6b6ad567153402a85d21bf Mon Sep 17 00:00:00 2001
From: Maxim Monastirsky <momonasmon@gmail.com>
Date: Sun, 7 Feb 2016 17:53:40 +0200
Subject: [PATCH 1/8] 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)
Change-Id: I96affa72412f3f38160fdca4b6efd20ca68d059f
---
framework/source/uielement/menubarmanager.cxx | 11 ++---
include/vcl/menu.hxx | 9 +---
vcl/inc/salmenu.hxx | 1 +
vcl/inc/unx/gtk/gtksalmenu.hxx | 4 +-
vcl/source/window/menu.cxx | 46 +++-----------------
vcl/unx/gtk/gloactiongroup.cxx | 2 +-
vcl/unx/gtk/gtkdata.cxx | 18 --------
vcl/unx/gtk/gtksalmenu.cxx | 62 +++++++--------------------
8 files changed, 31 insertions(+), 122 deletions(-)
diff --git a/framework/source/uielement/menubarmanager.cxx b/framework/source/uielement/menubarmanager.cxx
index 6a368a9..8ce02c6 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();
}
}
@@ -895,9 +893,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/include/vcl/menu.hxx b/include/vcl/menu.hxx
index ade703b..0d6e16a 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;
@@ -407,13 +409,6 @@ public:
};
-namespace vcl { namespace MenuInvalidator {
-
-VCL_DLLPUBLIC void AddMenuInvalidateListener(const Link<LinkParamNone*,void>&);
-VCL_DLLPUBLIC void Invalidated();
-
-}}
-
class VCL_DLLPUBLIC MenuBar : public Menu
{
Link<void*,void> maCloseHdl;
diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx
index 287e19e..468994a 100644
--- a/vcl/inc/salmenu.hxx
+++ b/vcl/inc/salmenu.hxx
@@ -80,6 +80,7 @@ public:
virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, FloatWinPopupFlags nFlags);
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/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx
index 5d9c262..8df2c1d 100644
--- a/vcl/inc/unx/gtk/gtksalmenu.hxx
+++ b/vcl/inc/unx/gtk/gtksalmenu.hxx
@@ -96,11 +96,11 @@ public:
void NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName );
void DispatchCommand( gint itemId, const gchar* aCommand );
- void Activate();
+ void Activate( const gchar* aMenuCommand = nullptr );
void Deactivate( const gchar* aMenuCommand );
void Display( bool bVisible );
bool PrepUpdate();
- void Update(); // Update this menu only.
+ virtual void Update() override; // Update this menu only.
void UpdateFull(); // Update full menu hierarchy from this menu.
};
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 3a6e54c..4aa7d41 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&)
{
}
@@ -3251,44 +3257,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/unx/gtk/gloactiongroup.cxx b/vcl/unx/gtk/gloactiongroup.cxx
index 8385388..e710809 100644
--- a/vcl/unx/gtk/gloactiongroup.cxx
+++ b/vcl/unx/gtk/gloactiongroup.cxx
@@ -201,7 +201,7 @@ g_lo_action_group_perform_submenu_action (GLOActionGroup *group,
SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " to " << bState);
if (bState)
- pSalMenu->Activate();
+ pSalMenu->Activate (action_name);
else
pSalMenu->Deactivate (action_name);
}
diff --git a/vcl/unx/gtk/gtkdata.cxx b/vcl/unx/gtk/gtkdata.cxx
index b5c64e0..9ea6dc9 100644
--- a/vcl/unx/gtk/gtkdata.cxx
+++ b/vcl/unx/gtk/gtkdata.cxx
@@ -1030,22 +1030,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/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index a0cc977..fd29a25 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -372,54 +372,9 @@ void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsig
pItem->mpSubMenu = pGtkSubMenu;
}
-static bool bInvalidMenus = false;
-static gboolean RefreshMenusUnity(gpointer)
-{
- 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();
- }
- }
-#endif
- bInvalidMenus = false;
- return FALSE;
-}
-
-static void RefreshMenusUnity(void*, LinkParamNone*)
-{
- if(!bInvalidMenus) {
- g_timeout_add(10, &RefreshMenusUnity, nullptr);
- bInvalidMenus = true;
- }
-}
-
-static Link<LinkParamNone*,void>* getRefreshLinkInstance()
-{
- static Link<LinkParamNone*,void>* pLink = nullptr;
- if(!pLink) {
- pLink = new Link<LinkParamNone*,void>(nullptr, &RefreshMenusUnity);
- }
- return pLink;
-}
-
void GtkSalMenu::SetFrame( const SalFrame* pFrame )
{
SolarMutexGuard aGuard;
- {
- vcl::MenuInvalidator::AddMenuInvalidateListener(*getRefreshLinkInstance());
- }
-
assert(mbMenuBar);
SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
mpFrame = static_cast< const GtkSalFrame* >( pFrame );
@@ -674,6 +629,7 @@ void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
{
pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
+ pMenuBar->HandleMenuDeActivateEvent(mpVCLMenu);
for ( size_t nPos = 0; nPos < maItems.size(); nPos++ )
{
GtkSalMenuItem *pSalItem = maItems[ nPos ];
@@ -685,11 +641,23 @@ void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
}
}
-void GtkSalMenu::Activate()
+void GtkSalMenu::Activate( const gchar* aMenuCommand )
{
if ( !mbMenuBar )
return;
- ActivateAllSubmenus(static_cast<MenuBar*>(mpVCLMenu));
+
+ if ( !aMenuCommand ) {
+ ActivateAllSubmenus( static_cast< MenuBar* >( mpVCLMenu ) );
+ return;
+ }
+
+ GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), TRUE );
+
+ if ( pSalSubMenu != nullptr ) {
+ MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
+ pMenuBar->HandleMenuActivateEvent( pSalSubMenu->mpVCLMenu );
+ pSalSubMenu->Update();
+ }
}
void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
--
2.7.1

@ -0,0 +1,172 @@
From 684f2191c0010aaa5c802a306bef209660df0968 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Mon, 22 Feb 2016 20:57:52 +0000
Subject: [PATCH 2/8] 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
Change-Id: Ibb1b5354d5e1483327f172d6890e134f1e4b9ee4
(cherry picked from commit c13a0b1f9e76584a4ffaea0ba754c8f9a01793d8)
---
include/vcl/menu.hxx | 8 ++++----
vcl/inc/unx/gtk/gtksalmenu.hxx | 4 ++--
vcl/source/window/menu.cxx | 15 +++++++--------
vcl/unx/gtk/gtksalmenu.cxx | 13 +++++--------
4 files changed, 18 insertions(+), 22 deletions(-)
diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx
index 0d6e16a..1dbd85e 100644
--- a/include/vcl/menu.hxx
+++ b/include/vcl/menu.hxx
@@ -406,8 +406,11 @@ public:
void HighlightItem( sal_uInt16 nItemPos );
void DeHighlight() { HighlightItem( 0xFFFF ); } // MENUITEMPOS_INVALID
-};
+ bool HandleMenuCommandEvent(Menu *pMenu, sal_uInt16 nEventId) const;
+ bool HandleMenuActivateEvent(Menu *pMenu) const;
+ bool HandleMenuDeActivateEvent(Menu *pMenu) const;
+};
class VCL_DLLPUBLIC MenuBar : public Menu
{
@@ -458,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; }
diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx
index 8df2c1d..1d58b7a 100644
--- a/vcl/inc/unx/gtk/gtksalmenu.hxx
+++ b/vcl/inc/unx/gtk/gtksalmenu.hxx
@@ -42,7 +42,7 @@ class GtkSalMenu : public SalMenu
private:
std::vector< GtkSalMenuItem* > maItems;
- bool mbMenuBar;
+ bool mbMenuBar;
Menu* mpVCLMenu;
GtkSalMenu* mpParentSalMenu;
const GtkSalFrame* mpFrame;
@@ -53,7 +53,7 @@ private:
GtkSalMenu* GetMenuForItemCommand( gchar* aCommand, gboolean bGetSubmenu );
void ImplUpdate( gboolean bRecurse );
- void ActivateAllSubmenus(MenuBar* pMenuBar);
+ void ActivateAllSubmenus(Menu* pMenuBar);
public:
GtkSalMenu( bool bMenuBar );
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 4aa7d41..999085e 100644
--- a/vcl/source/window/menu.cxx
+++ b/vcl/source/window/menu.cxx
@@ -2679,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();
@@ -2696,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() )
@@ -2735,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;
}
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index fd29a25..6887ade 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -622,11 +622,10 @@ void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aCommand), FALSE );
Menu* pSubMenu = ( pSalSubMenu != nullptr ) ? pSalSubMenu->GetMenu() : nullptr;
- MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
- pMenuBar->HandleMenuCommandEvent( pSubMenu, itemId );
+ mpVCLMenu->HandleMenuCommandEvent( pSubMenu, itemId );
}
-void GtkSalMenu::ActivateAllSubmenus(MenuBar* pMenuBar)
+void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar)
{
pMenuBar->HandleMenuActivateEvent(mpVCLMenu);
pMenuBar->HandleMenuDeActivateEvent(mpVCLMenu);
@@ -647,15 +646,14 @@ void GtkSalMenu::Activate( const gchar* aMenuCommand )
return;
if ( !aMenuCommand ) {
- ActivateAllSubmenus( static_cast< MenuBar* >( mpVCLMenu ) );
+ ActivateAllSubmenus(mpVCLMenu);
return;
}
GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), TRUE );
if ( pSalSubMenu != nullptr ) {
- MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
- pMenuBar->HandleMenuActivateEvent( pSalSubMenu->mpVCLMenu );
+ mpVCLMenu->HandleMenuActivateEvent( pSalSubMenu->mpVCLMenu );
pSalSubMenu->Update();
}
}
@@ -668,8 +666,7 @@ void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), TRUE );
if ( pSalSubMenu != nullptr ) {
- MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
- pMenuBar->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
+ mpVCLMenu->HandleMenuDeActivateEvent( pSalSubMenu->mpVCLMenu );
}
}
--
2.7.1

@ -0,0 +1,81 @@
From 67d1d0061c1cf5bb8aba0de75776435150c8559e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Tue, 23 Feb 2016 14:42:29 +0000
Subject: [PATCH 3/8] 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)
Change-Id: I59be60de5742d1e382cabefcbf0d8cdd5fc30b00
---
include/vcl/menu.hxx | 1 +
vcl/source/window/menu.cxx | 24 +++++++++++++++---------
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx
index 1dbd85e..f10141b 100644
--- a/include/vcl/menu.hxx
+++ b/include/vcl/menu.hxx
@@ -515,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/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 999085e..b753fac 100644
--- a/vcl/source/window/menu.cxx
+++ b/vcl/source/window/menu.cxx
@@ -2933,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() ) )
@@ -3097,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();
@@ -3180,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;
--
2.7.1

@ -0,0 +1,77 @@
From 1d40ccd2f55173f954e596ce59bc4307eceae453 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Wed, 24 Feb 2016 10:40:10 +0000
Subject: [PATCH 4/8] 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
Change-Id: I6a6ce94126253396cc273834a7e8a4fb0a56921d
(cherry picked from commit 36bddcbaa2d1673c1331c788eae9534aca2c5ec3)
---
vcl/unx/gtk/gtksalmenu.cxx | 45 +++++++++++++++++++++++----------------------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index 6887ade..a9a0932 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -530,30 +530,31 @@ void GtkSalMenu::NativeSetItemCommand( unsigned nSection,
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 );
--
2.7.1

@ -0,0 +1,32 @@
From 33d05b5ca87d1ddb76fef373a92833d07c00cb15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Wed, 24 Feb 2016 12:19:49 +0000
Subject: [PATCH 5/8] gtk3: handle items without commands
e.g. the draw/impress context menus. Handle these like
MenuManager::Activate does
Change-Id: I02a0e377a2d3a57ac7ac9239aaa75dbb856489d2
(cherry picked from commit b8ee342576b707dbffe877f5c225b640ee65276d)
---
vcl/unx/gtk/gtksalmenu.cxx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index a9a0932..346e42d 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -52,7 +52,9 @@ static gchar* GetCommandForItem( GtkSalMenuItem* pSalMenuItem, gchar* aCurrentCo
if ( !pMenu )
return nullptr;
- OUString aMenuCommand = pMenu->GetItemCommand( nId );
+ OUString aMenuCommand = pMenu->GetItemCommand(nId);
+ if (aMenuCommand.isEmpty())
+ aMenuCommand = "slot:" + OUString::number(nId);
gchar* aCommandStr = g_strdup( OUStringToOString( aMenuCommand, RTL_TEXTENCODING_UTF8 ).getStr() );
aCommand = g_strdup( aCommandStr );
--
2.7.1

@ -0,0 +1,64 @@
From 7dc5977edb7e34aed68aeb68846dee227f1a32de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Wed, 24 Feb 2016 13:02:40 +0000
Subject: [PATCH 6/8] 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
Change-Id: I7168b44d59fd64dfe264ed8ca26355252d697251
(cherry picked from commit 13917e0755bb864f22d0cf75a43854acbdb1eaec)
---
vcl/source/window/toolbox2.cxx | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/vcl/source/window/toolbox2.cxx b/vcl/source/window/toolbox2.cxx
index 0c916f6..663b7d6 100644
--- a/vcl/source/window/toolbox2.cxx
+++ b/vcl/source/window/toolbox2.cxx
@@ -1791,6 +1791,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
@@ -1826,7 +1840,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 );
@@ -1843,7 +1858,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 );
--
2.7.1

@ -0,0 +1,26 @@
From 205dd9979f77c709cf36b92da1836ce5374879ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Wed, 24 Feb 2016 15:24:53 +0000
Subject: [PATCH 7/8] set gtk layout direction to match ours
Change-Id: I27610f28f42368355bef1b3461fc3ccea1b07218
(cherry picked from commit b50071c817657866f8b22873be26d34970005a2d)
---
vcl/unx/gtk/gtkdata.cxx | 1 +
1 file changed, 1 insertion(+)
diff --git a/vcl/unx/gtk/gtkdata.cxx b/vcl/unx/gtk/gtkdata.cxx
index 9ea6dc9..f15cce2 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()
--
2.7.1

@ -0,0 +1,550 @@
From b265bcddde36bea2f5b31ce5df407301cbfe82b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Tue, 23 Feb 2016 15:57:11 +0000
Subject: [PATCH 8/8] 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
Change-Id: I4336391718844bc73cfc47c1043f99f0e3b812d8
(cherry picked from commit a0c700b1493c7b51540d1e77b44d1edd9bf920f0)
---
vcl/inc/unx/gtk/gloactiongroup.h | 3 +
vcl/inc/unx/gtk/gtksalmenu.hxx | 9 +-
vcl/unx/gtk/gloactiongroup.cxx | 43 ++++----
vcl/unx/gtk/gtksalmenu.cxx | 228 +++++++++++++++++++++++++++++----------
4 files changed, 203 insertions(+), 80 deletions(-)
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/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx
index 1d58b7a..d95d25c 100644
--- a/vcl/inc/unx/gtk/gtksalmenu.hxx
+++ b/vcl/inc/unx/gtk/gtksalmenu.hxx
@@ -43,16 +43,17 @@ private:
std::vector< GtkSalMenuItem* > maItems;
bool mbMenuBar;
+ bool mbMenuVisibility;
Menu* mpVCLMenu;
GtkSalMenu* mpParentSalMenu;
- const GtkSalFrame* mpFrame;
+ GtkSalFrame* mpFrame;
// GMenuModel and GActionGroup attributes
GMenuModel* mpMenuModel;
GActionGroup* mpActionGroup;
GtkSalMenu* GetMenuForItemCommand( gchar* aCommand, gboolean bGetSubmenu );
- void ImplUpdate( gboolean bRecurse );
+ void ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries);
void ActivateAllSubmenus(Menu* pMenuBar);
public:
@@ -77,7 +78,7 @@ 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; }
@@ -102,6 +103,8 @@ public:
bool PrepUpdate();
virtual void Update() override; // Update this menu only.
void UpdateFull(); // Update full menu hierarchy from this menu.
+
+ virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, FloatWinPopupFlags nFlags) override;
};
class GtkSalMenuItem : public SalMenuItem
diff --git a/vcl/unx/gtk/gloactiongroup.cxx b/vcl/unx/gtk/gloactiongroup.cxx
index e710809..110e0dc 100644
--- a/vcl/unx/gtk/gloactiongroup.cxx
+++ b/vcl/unx/gtk/gloactiongroup.cxx
@@ -100,8 +100,9 @@ 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. */
+ GtkSalMenu *topmenu; /* TopLevel Menu to which GActionGroup is associated. */
};
static void g_lo_action_group_iface_init (GActionGroupInterface *);
@@ -187,13 +188,7 @@ g_lo_action_group_perform_submenu_action (GLOActionGroup *group,
GVariant *state)
{
- 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());
+ GtkSalMenu* pSalMenu = group->priv->topmenu;
SAL_INFO("vcl.unity", "g_lo_action_group_perform_submenu_action on " << group << " for menu " << pSalMenu);
if (pSalMenu != nullptr) {
@@ -263,23 +258,18 @@ g_lo_action_group_activate (GActionGroup *group,
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);
+ GtkSalMenu* pSalMenu = lo_group->priv->topmenu;
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);
+ 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 ( 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 );
}
}
@@ -355,6 +345,17 @@ g_lo_action_group_init (GLOActionGroup *group)
group->priv->table = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
group->priv->frame = nullptr;
+ group->priv->topmenu = nullptr;
+}
+
+void
+g_lo_action_group_set_top_menu (GLOActionGroup *group,
+ gpointer top_menu)
+{
+ group->priv = G_TYPE_INSTANCE_GET_PRIVATE (group,
+ G_TYPE_LO_ACTION_GROUP,
+ GLOActionGroupPrivate);
+ group->priv->topmenu = static_cast<GtkSalMenu*>(top_menu);
}
static void
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index 346e42d..7bc9232 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,6 +25,7 @@
#endif
#include <sal/log.hxx>
+#include <window.h>
// FIXME Copied from framework/inc/framework/menuconfiguration.hxx to
// avoid circular dependency between modules. It should be in a common
@@ -31,8 +33,6 @@
const sal_uInt16 START_ITEMID_WINDOWLIST = 4600;
const sal_uInt16 END_ITEMID_WINDOWLIST = 4699;
-static bool bMenuVisibility = false;
-
/*
* This function generates the proper command name for all actions, including
* duplicated or special ones.
@@ -77,20 +77,17 @@ static gchar* GetCommandForItem( GtkSalMenuItem* pSalMenuItem, gchar* aCurrentCo
bool GtkSalMenu::PrepUpdate()
{
- const GtkSalFrame* pFrame = GetFrame();
- if (pFrame)
- {
- GtkSalFrame* pNonConstFrame = const_cast<GtkSalFrame*>(pFrame);
- GtkSalMenu* pSalMenu = this;
-
- if ( !pNonConstFrame->GetMenu() )
- pNonConstFrame->SetMenu( pSalMenu );
+ bool bMenuVisibility;
- if ( bMenuVisibility && mpMenuModel && mpActionGroup )
- return true;
- }
+ //get top level visibility
+ const GtkSalMenu* pMenu = this;
+ do
+ {
+ bMenuVisibility = pMenu->mbMenuVisibility;
+ pMenu = pMenu->mpParentSalMenu;
+ } while (pMenu);
- return false;
+ return bMenuVisibility && mpMenuModel && mpActionGroup;
}
/*
@@ -114,14 +111,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 );
@@ -173,7 +214,7 @@ void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList,
}
}
-void GtkSalMenu::ImplUpdate( gboolean bRecurse )
+void GtkSalMenu::ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries)
{
SolarMutexGuard aGuard;
@@ -277,7 +318,7 @@ void GtkSalMenu::ImplUpdate( gboolean bRecurse )
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(bRecurse, bRemoveDisabledEntries);
}
}
@@ -287,6 +328,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 );
@@ -299,12 +346,89 @@ 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);
}
void GtkSalMenu::UpdateFull()
{
- ImplUpdate( TRUE );
+ //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(true, !pMenu->mbMenuBar);
+}
+
+bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const Rectangle& /*rRect*/,
+ FloatWinPopupFlags /*nFlags*/)
+{
+#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();
+ }
+
+ Display(true);
+
+ mpFrame = static_cast<GtkSalFrame*>(pWin->ImplGetWindowImpl()->mpRealParent->ImplGetFrame());
+
+ GLOActionGroup* pActionGroup = g_lo_action_group_new(static_cast<gpointer>(mpFrame));
+ g_lo_action_group_set_top_menu(pActionGroup, static_cast<gpointer>(this));
+
+ 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, nullptr, nullptr, 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;
+ return false;
+#endif
}
/*
@@ -313,6 +437,7 @@ void GtkSalMenu::UpdateFull()
GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
mbMenuBar( bMenuBar ),
+ mbMenuVisibility( false ),
mpVCLMenu( nullptr ),
mpParentSalMenu( nullptr ),
mpFrame( nullptr ),
@@ -321,25 +446,28 @@ GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
{
}
+void GtkSalMenu::SetMenuModel(GMenuModel* pMenuModel)
+{
+ if (mpMenuModel)
+ g_object_unref(mpMenuModel);
+ mpMenuModel = pMenuModel;
+ if (mpMenuModel)
+ g_object_ref(mpMenuModel);
+}
+
GtkSalMenu::~GtkSalMenu()
{
SolarMutexGuard aGuard;
- if ( mbMenuBar )
- {
- if ( mpMenuModel )
- {
-// g_lo_menu_remove( G_LO_MENU( mpMenuModel ), 0 );
- g_object_unref( mpMenuModel );
- }
- }
+ if (mpMenuModel)
+ g_object_unref(mpMenuModel);
maItems.clear();
}
bool GtkSalMenu::VisibleMenuBar()
{
- return bMenuVisibility;
+ return mbMenuBar && mbMenuVisibility;
}
void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
@@ -374,22 +502,21 @@ void GtkSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsig
pItem->mpSubMenu = pGtkSubMenu;
}
-void GtkSalMenu::SetFrame( const SalFrame* pFrame )
+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" ) );
@@ -407,11 +534,12 @@ void GtkSalMenu::SetFrame( const SalFrame* pFrame )
if ( pActionGroup )
{
g_lo_action_group_clear( pActionGroup );
+ g_lo_action_group_set_top_menu(pActionGroup, static_cast<gpointer>(this));
mpActionGroup = G_ACTION_GROUP( pActionGroup );
}
// Generate the main menu structure.
- if (bMenuVisibility)
+ if (mbMenuVisibility)
UpdateFull();
g_lo_menu_insert_section( pMenuModel, 0, nullptr, mpMenuModel );
@@ -618,14 +746,9 @@ GtkSalMenu* GtkSalMenu::GetMenuForItemCommand( gchar* aCommand, gboolean bGetSub
void GtkSalMenu::DispatchCommand( gint itemId, const gchar *aCommand )
{
SolarMutexGuard aGuard;
- // Only the menubar is allowed to dispatch commands.
- if ( !mbMenuBar )
- return;
-
GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aCommand), FALSE );
Menu* pSubMenu = ( pSalSubMenu != nullptr ) ? pSalSubMenu->GetMenu() : nullptr;
-
- mpVCLMenu->HandleMenuCommandEvent( pSubMenu, itemId );
+ mpVCLMenu->HandleMenuCommandEvent(pSubMenu, itemId);
}
void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar)
@@ -645,9 +768,6 @@ void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar)
void GtkSalMenu::Activate( const gchar* aMenuCommand )
{
- if ( !mbMenuBar )
- return;
-
if ( !aMenuCommand ) {
ActivateAllSubmenus(mpVCLMenu);
return;
@@ -663,9 +783,6 @@ void GtkSalMenu::Activate( const gchar* aMenuCommand )
void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
{
- if ( !mbMenuBar )
- return;
-
GtkSalMenu* pSalSubMenu = GetMenuForItemCommand( const_cast<gchar*>(aMenuCommand), TRUE );
if ( pSalSubMenu != nullptr ) {
@@ -675,15 +792,14 @@ void GtkSalMenu::Deactivate( const gchar* aMenuCommand )
void GtkSalMenu::Display( bool bVisible )
{
- if ( !mbMenuBar || mpVCLMenu == nullptr )
- return;
+ mbMenuVisibility = bVisible;
- bMenuVisibility = bVisible;
-
- bool bVCLMenuVisible = !bVisible;
-
- MenuBar* pMenuBar = static_cast< MenuBar* >( mpVCLMenu );
- pMenuBar->SetDisplayable( bVCLMenuVisible );
+ if (mbMenuBar)
+ {
+ bool bVCLMenuVisible = !bVisible;
+ MenuBar* pMenuBar = static_cast<MenuBar*>(mpVCLMenu);
+ pMenuBar->SetDisplayable(bVCLMenuVisible);
+ }
}
bool GtkSalMenu::IsItemVisible( unsigned nPos )
--
2.7.1

@ -255,6 +255,14 @@ Patch20: 0003-rename-X11WindowProvider-to-a-NativeWindowHandle-pro.patch
Patch21: 0004-implement-wayland-handle-passing-for-gstreamer.patch Patch21: 0004-implement-wayland-handle-passing-for-gstreamer.patch
Patch22: 0005-gtk3-wayland-play-video-via-gtksink-gstreamer-elemen.patch Patch22: 0005-gtk3-wayland-play-video-via-gtksink-gstreamer-elemen.patch
Patch23: 0001-gtk3-get-app-menu-working-again-under-gtk3.patch Patch23: 0001-gtk3-get-app-menu-working-again-under-gtk3.patch
Patch24: 0001-tdf-97665-Let-s-hope-that-over-activation-isn-t-real.patch
Patch25: 0002-gtk3-some-changes-towards-enabling-native-gtk3-popup.patch
Patch26: 0003-gtk3-vcl-popups-flush-any-unexecuted-Select-events-o.patch
Patch27: 0004-gtk3-replace-old-action-if-same-command-is-added.patch
Patch28: 0005-gtk3-handle-items-without-commands.patch
Patch29: 0006-mark-checkable-toolbox-menu-entries-as-checkable.patch
Patch30: 0007-set-gtk-layout-direction-to-match-ours.patch
Patch21: 0008-gtk3-implement-native-context-menus.patch
%if 0%{?rhel} %if 0%{?rhel}
# not upstreamed # not upstreamed

Loading…
Cancel
Save