summaryrefslogtreecommitdiff
path: root/vcl/unx/gtk/gtksalmenu.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/unx/gtk/gtksalmenu.cxx')
-rw-r--r--vcl/unx/gtk/gtksalmenu.cxx1444
1 files changed, 0 insertions, 1444 deletions
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
deleted file mode 100644
index d0503519a084..000000000000
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ /dev/null
@@ -1,1444 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include <unx/gtk/gtksalmenu.hxx>
-
-#ifdef ENABLE_GMENU_INTEGRATION
-
-#include <unx/gendata.hxx>
-#include <unx/saldisp.hxx>
-#include <unx/gtk/gtkdata.hxx>
-#include <unx/gtk/glomenu.h>
-#include <unx/gtk/gloactiongroup.h>
-#include <vcl/floatwin.hxx>
-#include <vcl/menu.hxx>
-#include <vcl/pngwrite.hxx>
-#include <unx/gtk/gtkinst.hxx>
-
-#include <sal/log.hxx>
-#include <tools/stream.hxx>
-#include <window.h>
-#include <strings.hrc>
-
-static bool bUnityMode = false;
-
-/*
- * This function generates a unique command name for each menu item
- */
-static gchar* GetCommandForItem(GtkSalMenu* pParentMenu, sal_uInt16 nItemId)
-{
- OString aCommand("window-");
- aCommand = aCommand + OString::number(reinterpret_cast<unsigned long>(pParentMenu));
- aCommand = aCommand + "-" + OString::number(nItemId);
- return g_strdup(aCommand.getStr());
-}
-
-static gchar* GetCommandForItem(GtkSalMenuItem* pSalMenuItem)
-{
- return GetCommandForItem(pSalMenuItem->mpParentMenu,
- pSalMenuItem->mnId);
-}
-
-bool GtkSalMenu::PrepUpdate()
-{
-#if GTK_CHECK_VERSION(3,0,0)
- return mpMenuModel && mpActionGroup;
-#else
- return bUnityMode && mpMenuModel && mpActionGroup;
-#endif
-}
-
-/*
- * Menu updating methods
- */
-
-static void RemoveSpareItemsFromNativeMenu( GLOMenu* pMenu, GList** pOldCommandList, unsigned nSection, unsigned nValidItems )
-{
- sal_Int32 nSectionItems = g_lo_menu_get_n_items_from_section( pMenu, nSection );
-
- while ( nSectionItems > static_cast<sal_Int32>(nValidItems) )
- {
- gchar* aCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, --nSectionItems );
-
- if ( aCommand != nullptr && pOldCommandList != nullptr )
- *pOldCommandList = g_list_append( *pOldCommandList, g_strdup( aCommand ) );
-
- g_free( aCommand );
-
- g_lo_menu_remove_from_section( pMenu, nSection, nSectionItems );
- }
-}
-
-typedef std::pair<GtkSalMenu*, sal_uInt16> MenuAndId;
-
-namespace
-{
- MenuAndId decode_command(const gchar *action_name)
- {
- 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 MenuAndId(pSalSubMenu, sItemId.toInt32());
- }
-}
-
-static 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);
- 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));
- if (nSubMenuSections == 0)
- bRemove = true;
- else if (nSubMenuSections == 1)
- {
- gint nItems = g_lo_menu_get_n_items_from_section(pSubMenuModel, 0);
- if (nItems == 0)
- bRemove = true;
- else if (nItems == 1)
- {
- //If the only entry is the "No Selection Possible" entry, then we are allowed
- //to removed it
- gchar* pSubCommand = g_lo_menu_get_command_from_item_in_section(pSubMenuModel, 0, 0);
- MenuAndId aMenuAndId(decode_command(pSubCommand));
- bRemove = aMenuAndId.second == 0xFFFF;
- g_free(pSubCommand);
- }
- }
- }
- }
-
- 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;
- }
-}
-
-static 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 > nLastSection; n--)
- {
- RemoveSpareItemsFromNativeMenu( pMenu, pOldCommandList, n, 0 );
- g_lo_menu_remove( pMenu, n );
- }
-}
-
-static gint CompareStr( gpointer str1, gpointer str2 )
-{
- return g_strcmp0( static_cast<const gchar*>(str1), static_cast<const gchar*>(str2) );
-}
-
-static void RemoveUnusedCommands( GLOActionGroup* pActionGroup, GList* pOldCommandList, GList* pNewCommandList )
-{
- if ( pActionGroup == nullptr || pOldCommandList == nullptr )
- {
- g_list_free_full( pOldCommandList, g_free );
- g_list_free_full( pNewCommandList, g_free );
- return;
- }
-
- while ( pNewCommandList != nullptr )
- {
- GList* pNewCommand = g_list_first( pNewCommandList );
- pNewCommandList = g_list_remove_link( pNewCommandList, pNewCommand );
-
- gpointer aCommand = g_list_nth_data( pNewCommand, 0 );
-
- GList* pOldCommand = g_list_find_custom( pOldCommandList, aCommand, reinterpret_cast<GCompareFunc>(CompareStr) );
-
- if ( pOldCommand != nullptr )
- {
- pOldCommandList = g_list_remove_link( pOldCommandList, pOldCommand );
- g_list_free_full( pOldCommand, g_free );
- }
-
- g_list_free_full( pNewCommand, g_free );
- }
-
- while ( pOldCommandList != nullptr )
- {
- GList* pCommand = g_list_first( pOldCommandList );
- pOldCommandList = g_list_remove_link( pOldCommandList, pCommand );
-
- gchar* aCommand = static_cast<gchar*>(g_list_nth_data( pCommand, 0 ));
-
- g_lo_action_group_remove( pActionGroup, aCommand );
-
- g_list_free_full( pCommand, g_free );
- }
-}
-
-void GtkSalMenu::ImplUpdate(bool bRecurse, bool bRemoveDisabledEntries)
-{
- SolarMutexGuard aGuard;
-
- SAL_INFO("vcl.unity", "ImplUpdate pre PrepUpdate");
- if( !PrepUpdate() )
- return;
-
- if (mbNeedsUpdate)
- {
- mbNeedsUpdate = false;
- if (mbMenuBar && maUpdateMenuBarIdle.IsActive())
- {
- maUpdateMenuBarIdle.Stop();
- maUpdateMenuBarIdle.Invoke();
- return;
- }
- }
-
- Menu* pVCLMenu = mpVCLMenu;
- GLOMenu* pLOMenu = G_LO_MENU( mpMenuModel );
- GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
- SAL_INFO("vcl.unity", "Syncing vcl menu " << pVCLMenu << " to menu model " << pLOMenu << " and action group " << pActionGroup);
- GList *pOldCommandList = nullptr;
- GList *pNewCommandList = nullptr;
-
- sal_uInt16 nLOMenuSize = g_menu_model_get_n_items( G_MENU_MODEL( pLOMenu ) );
-
- if ( nLOMenuSize == 0 )
- g_lo_menu_new_section( pLOMenu, 0, nullptr );
-
- sal_Int32 nSection = 0;
- sal_Int32 nItemPos = 0;
- sal_Int32 validItems = 0;
- sal_Int32 nItem;
-
- for ( nItem = 0; nItem < static_cast<sal_Int32>(GetItemCount()); nItem++ ) {
- if ( !IsItemVisible( nItem ) )
- continue;
-
- GtkSalMenuItem *pSalMenuItem = GetItemAtPos( nItem );
- sal_uInt16 nId = pSalMenuItem->mnId;
-
- // PopupMenu::ImplExecute might add <No Selection Possible> entry to top-level
- // popup menu, but we have our own implementation below, so skip that one.
- if ( nId == 0xFFFF )
- continue;
-
- if ( pSalMenuItem->mnType == MenuItemType::SEPARATOR )
- {
- // Delete extra items from current section.
- RemoveSpareItemsFromNativeMenu( pLOMenu, &pOldCommandList, nSection, validItems );
-
- nSection++;
- nItemPos = 0;
- validItems = 0;
-
- if ( nLOMenuSize <= nSection )
- {
- g_lo_menu_new_section( pLOMenu, nSection, nullptr );
- nLOMenuSize++;
- }
-
- continue;
- }
-
- if ( nItemPos >= g_lo_menu_get_n_items_from_section( pLOMenu, nSection ) )
- g_lo_menu_insert_in_section( pLOMenu, nSection, nItemPos, "EMPTY STRING" );
-
- // Get internal menu item values.
- OUString aText = pVCLMenu->GetItemText( nId );
- Image aImage = pVCLMenu->GetItemImage( nId );
- bool bEnabled = pVCLMenu->IsItemEnabled( nId );
- vcl::KeyCode nAccelKey = pVCLMenu->GetAccelKey( nId );
- bool bChecked = pVCLMenu->IsItemChecked( nId );
- MenuItemBits itemBits = pVCLMenu->GetItemBits( nId );
-
- // Store current item command in command list.
- gchar *aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pLOMenu, nSection, nItemPos );
-
- if ( aCurrentCommand != nullptr )
- pOldCommandList = g_list_append( pOldCommandList, aCurrentCommand );
-
- // Get the new command for the item.
- gchar* aNativeCommand = GetCommandForItem(pSalMenuItem);
-
- // Force updating of native menu labels.
- NativeSetItemText( nSection, nItemPos, aText );
- NativeSetItemIcon( nSection, nItemPos, aImage );
- NativeSetAccelerator( nSection, nItemPos, nAccelKey, nAccelKey.GetName( GetFrame()->GetWindow() ) );
-
- if ( g_strcmp0( aNativeCommand, "" ) != 0 && pSalMenuItem->mpSubMenu == nullptr )
- {
- NativeSetItemCommand( nSection, nItemPos, nId, aNativeCommand, itemBits, bChecked, false );
- NativeCheckItem( nSection, nItemPos, itemBits, bChecked );
- NativeSetEnableItem( aNativeCommand, bEnabled );
-
- pNewCommandList = g_list_append( pNewCommandList, g_strdup( aNativeCommand ) );
- }
-
- GtkSalMenu* pSubmenu = pSalMenuItem->mpSubMenu;
-
- if ( pSubmenu && pSubmenu->GetMenu() )
- {
- 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 );
-
- if ( pSubMenuModel == nullptr )
- {
- g_lo_menu_new_submenu_in_item_in_section( pLOMenu, nSection, nItemPos );
- pSubMenuModel = g_lo_menu_get_submenu_from_item_in_section( pLOMenu, nSection, nItemPos );
- }
-
- g_object_unref( pSubMenuModel );
-
- 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(true, bRemoveDisabledEntries);
- }
- }
-
- g_free( aNativeCommand );
-
- ++nItemPos;
- ++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 );
-
- // Delete extra sections.
- RemoveSpareSectionsFromNativeMenu( pLOMenu, &pOldCommandList, nSection );
-
- // Delete unused commands.
- RemoveUnusedCommands( pActionGroup, pOldCommandList, pNewCommandList );
-
- // Resolves: tdf#103166 if the menu is empty, add a disabled
- // <No Selection Possible> placeholder.
- sal_Int32 nSectionsCount = g_menu_model_get_n_items(G_MENU_MODEL(pLOMenu));
- gint nItemsCount = 0;
- for (nSection = 0; nSection < nSectionsCount; ++nSection)
- {
- nItemsCount += g_lo_menu_get_n_items_from_section(pLOMenu, nSection);
- if (nItemsCount)
- break;
- }
- if (!nItemsCount)
- {
- gchar* aNativeCommand = GetCommandForItem(this, 0xFFFF);
- OUString aPlaceholderText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
- g_lo_menu_insert_in_section(pLOMenu, nSection-1, 0,
- OUStringToOString(aPlaceholderText, RTL_TEXTENCODING_UTF8).getStr());
- NativeSetItemCommand(nSection-1, 0, 0xFFFF, aNativeCommand, MenuItemBits::NONE, false, false);
- NativeSetEnableItem(aNativeCommand, false);
- g_free(aNativeCommand);
- }
-}
-
-void GtkSalMenu::Update()
-{
- //find out if top level is a menubar or not, if not, then it's 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
-
-bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect,
- FloatWinPopupFlags nFlags)
-{
-#if GTK_CHECK_VERSION(3,0,0)
- VclPtr<vcl::Window> xParent = pWin->ImplGetWindowImpl()->mpRealParent;
- mpFrame = static_cast<GtkSalFrame*>(xParent->ImplGetFrame());
-
- GLOActionGroup* pActionGroup = g_lo_action_group_new();
- 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);
-
-#if GTK_CHECK_VERSION(3,22,0)
- if (gtk_check_version(3, 22, 0) == nullptr)
- {
- GdkGravity rect_anchor = GDK_GRAVITY_SOUTH_WEST, menu_anchor = GDK_GRAVITY_NORTH_WEST;
-
- if (nFlags & FloatWinPopupFlags::Left)
- {
- rect_anchor = GDK_GRAVITY_NORTH_WEST;
- menu_anchor = GDK_GRAVITY_NORTH_EAST;
- }
- else if (nFlags & FloatWinPopupFlags::Up)
- {
- rect_anchor = GDK_GRAVITY_NORTH_WEST;
- menu_anchor = GDK_GRAVITY_SOUTH_WEST;
- }
- else if (nFlags & FloatWinPopupFlags::Right)
- {
- rect_anchor = GDK_GRAVITY_NORTH_EAST;
- }
-
- tools::Rectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(xParent, rRect);
- aFloatRect.Move(-mpFrame->maGeometry.nX, -mpFrame->maGeometry.nY);
- GdkRectangle rect {static_cast<int>(aFloatRect.Left()), static_cast<int>(aFloatRect.Top()),
- static_cast<int>(aFloatRect.GetWidth()), static_cast<int>(aFloatRect.GetHeight())};
-
- GdkWindow* gdkWindow = widget_get_window(mpFrame->getMouseEventWidget());
- gtk_menu_popup_at_rect(GTK_MENU(pWidget), gdkWindow, &rect, rect_anchor, menu_anchor, nullptr);
- }
- else
-#endif
- {
- 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 = GtkSalFrame::GetLastInputEventTime();
- }
-
- // 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);
-
- 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);
-
- mpVCLMenu->Deactivate();
-
- gtk_widget_insert_action_group(mpFrame->getMouseEventWidget(), "win", nullptr);
-
- gtk_widget_destroy(pWidget);
-
- g_object_unref(mpActionGroup);
- ClearActionGroupAndMenuModel();
-
- mpFrame = nullptr;
-
- return true;
-#else
- (void)pWin;
- (void)rRect;
- (void)nFlags;
- return false;
-#endif
-}
-
-/*
- * GtkSalMenu
- */
-
-GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
- mbInActivateCallback( false ),
- mbMenuBar( bMenuBar ),
- mbNeedsUpdate( false ),
- mbReturnFocusToDocument( false ),
- mbAddedGrab( false ),
- mpMenuBarContainerWidget( nullptr ),
- mpMenuAllowShrinkWidget( nullptr ),
- mpMenuBarWidget( nullptr ),
- mpMenuBarContainerProvider( nullptr ),
- mpMenuBarProvider( 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(TaskPriority::HIGHEST);
- maUpdateMenuBarIdle.SetInvokeHandler(LINK(this, GtkSalMenu, MenuBarHierarchyChangeHandler));
- maUpdateMenuBarIdle.SetDebugName("Native Gtk Menu Update Idle");
-}
-
-IMPL_LINK_NOARG(GtkSalMenu, MenuBarHierarchyChangeHandler, Timer *, void)
-{
- SAL_WARN_IF(!mpFrame, "vcl.gtk", "MenuBar layout changed, but no frame for some reason!");
- if (!mpFrame)
- return;
- SetFrame(mpFrame);
-}
-
-void GtkSalMenu::SetNeedsUpdate()
-{
- GtkSalMenu* pMenu = this;
- // start that the menu and its parents are in need of an update
- // on the next activation
- while (pMenu && !pMenu->mbNeedsUpdate)
- {
- pMenu->mbNeedsUpdate = true;
- pMenu = pMenu->mpParentSalMenu;
- }
- // only if a menubar is directly updated do we force in a full
- // structure update
- if (mbMenuBar && !maUpdateMenuBarIdle.IsActive())
- maUpdateMenuBarIdle.Start();
-}
-
-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();
-
- if (mpFrame)
- mpFrame->SetMenu(nullptr);
-}
-
-bool GtkSalMenu::VisibleMenuBar()
-{
- return mbMenuBar && (bUnityMode || mpMenuBarContainerWidget);
-}
-
-void GtkSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
-{
- SolarMutexGuard aGuard;
- GtkSalMenuItem *pItem = static_cast<GtkSalMenuItem*>( pSalMenuItem );
-
- if ( nPos == MENU_APPEND )
- maItems.push_back( pItem );
- else
- 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 )
-{
- SolarMutexGuard aGuard;
- GtkSalMenuItem *pItem = static_cast< GtkSalMenuItem* >( pSalMenuItem );
- GtkSalMenu *pGtkSubMenu = static_cast< GtkSalMenu* >( pSubMenu );
-
- if ( pGtkSubMenu == nullptr )
- return;
-
- pGtkSubMenu->mpParentSalMenu = this;
- pItem->mpSubMenu = pGtkSubMenu;
-
- SetNeedsUpdate();
-}
-
-#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)
-{
-#if GTK_CHECK_VERSION(3,0,0)
- assert(mbMenuBar);
- if (!mpMenuBarContainerWidget)
- return;
-
- if (!bShow)
- {
- if (mpCloseButton)
- gtk_widget_destroy(mpCloseButton);
- return;
- }
-
- MenuBar *pVclMenuBar = static_cast<MenuBar*>(mpVCLMenu.get());
- 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();
- static const gchar data[] = "* { "
- "padding: 0;"
- "margin-left: 8px;"
- "margin-right: 8px;"
- "min-width: 18px;"
- "min-height: 18px;"
- "}";
- const gchar olddata[] = "* { "
- "padding: 0;"
- "margin-left: 8px;"
- "margin-right: 8px;"
- "}";
- gtk_css_provider_load_from_data(pProvider, gtk_check_version(3, 20, 0) == nullptr ? data : olddata, -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(mpMenuBarContainerWidget), GTK_WIDGET(mpCloseButton), 1, 0, 1, 1);
- gtk_widget_show_all(mpCloseButton);
-#else
- (void)bShow;
- (void)mpMenuBarContainerWidget;
- (void)mpCloseButton;
-#endif
-}
-
-//Typically when the menubar is deactivated we want the focus to return
-//to where it came from. If the menubar was activated because of F6
-//moving focus into the associated VCL menubar then on pressing ESC
-//or any other normal reason for deactivation we want focus to return
-//to the document, definitely not still stuck in the associated
-//VCL menubar. But if F6 is pressed while the menubar is activated
-//we want to pass that F6 back to the VCL menubar which will move
-//focus to the next pane by itself.
-void GtkSalMenu::ReturnFocus()
-{
- if (mbAddedGrab)
- {
- gtk_grab_remove(mpMenuBarWidget);
- mbAddedGrab = false;
- }
- if (!mbReturnFocusToDocument)
- gtk_widget_grab_focus(GTK_WIDGET(mpFrame->getEventBox()));
- else
- mpFrame->GetWindow()->GrabFocusToDocument();
- mbReturnFocusToDocument = false;
-}
-
-gboolean GtkSalMenu::SignalKey(GdkEventKey const * pEvent)
-{
- if (pEvent->keyval == GDK_KEY_F6)
- {
- mbReturnFocusToDocument = false;
- gtk_menu_shell_cancel(GTK_MENU_SHELL(mpMenuBarWidget));
- //because we return false here, the keypress will continue
- //to propagate and in the case that vcl focus is in
- //the vcl menubar then that will also process F6 and move
- //to the next pane
- }
- return false;
-}
-
-//The GtkSalMenu is owner by a Vcl Menu/MenuBar. In the menubar
-//case the vcl menubar is present and "visible", but with a 0 height
-//so it not apparent. Normally it acts as though it is not there when
-//a Native menubar is active. If we return true here, then for keyboard
-//activation and traversal with F6 through panes then the vcl menubar
-//acts as though it *is* present and we translate its take focus and F6
-//traversal key events into the gtk menubar equivalents.
-bool GtkSalMenu::CanGetFocus() const
-{
- return mpMenuBarWidget != nullptr;
-}
-
-bool GtkSalMenu::TakeFocus()
-{
- if (!mpMenuBarWidget)
- return false;
-
- //Send a keyboard event to the gtk menubar to let it know it has been
- //activated via the keyboard. Doesn't do anything except cause the gtk
- //menubar "keyboard_mode" member to get set to true, so typically mnemonics
- //are shown which will serve as indication that the menubar has focus
- //(given that we want to show it with no menus popped down)
- GdkEvent *event = GtkSalFrame::makeFakeKeyPress(mpMenuBarWidget);
- gtk_widget_event(mpMenuBarWidget, event);
- gdk_event_free(event);
-
- //this pairing results in a menubar with keyboard focus with no menus
- //auto-popped down
- gtk_grab_add(mpMenuBarWidget);
- mbAddedGrab = true;
- gtk_menu_shell_select_first(GTK_MENU_SHELL(mpMenuBarWidget), false);
- gtk_menu_shell_deselect(GTK_MENU_SHELL(mpMenuBarWidget));
- mbReturnFocusToDocument = true;
- return true;
-}
-
-#if GTK_CHECK_VERSION(3,0,0)
-
-static void MenuBarReturnFocus(GtkMenuShell*, gpointer menu)
-{
- GtkSalFrame::UpdateLastInputEventTime(gtk_get_current_event_time());
- GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu);
- pMenu->ReturnFocus();
-}
-
-static gboolean MenuBarSignalKey(GtkWidget*, GdkEventKey* pEvent, gpointer menu)
-{
- GtkSalMenu* pMenu = static_cast<GtkSalMenu*>(menu);
- return pMenu->SignalKey(pEvent);
-}
-
-#endif
-
-void GtkSalMenu::CreateMenuBarWidget()
-{
-#if GTK_CHECK_VERSION(3,0,0)
- if (mpMenuBarContainerWidget)
- return;
-
- GtkGrid* pGrid = mpFrame->getTopLevelGridWidget();
- mpMenuBarContainerWidget = gtk_grid_new();
-
- gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarContainerWidget), true);
- gtk_grid_insert_row(pGrid, 0);
- gtk_grid_attach(pGrid, mpMenuBarContainerWidget, 0, 0, 1, 1);
-
- mpMenuAllowShrinkWidget = gtk_scrolled_window_new(nullptr, nullptr);
- gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_SHADOW_NONE);
- // tdf#116290 external policy on scrolledwindow will not show a scrollbar,
- // but still allow scrolled window to not be sized to the child content.
- // So the menubar can be shrunk past its nominal smallest width.
- // Unlike a hack using GtkFixed/GtkLayout the correct placement of the menubar occurs under RTL
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mpMenuAllowShrinkWidget), GTK_POLICY_EXTERNAL, GTK_POLICY_NEVER);
- gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), mpMenuAllowShrinkWidget, 0, 0, 1, 1);
-
- mpMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel);
-
- gtk_widget_insert_action_group(mpMenuBarWidget, "win", mpActionGroup);
- gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true);
- gtk_widget_set_hexpand(mpMenuAllowShrinkWidget, true);
- gtk_container_add(GTK_CONTAINER(mpMenuAllowShrinkWidget), mpMenuBarWidget);
-
- g_signal_connect(G_OBJECT(mpMenuBarWidget), "deactivate", G_CALLBACK(MenuBarReturnFocus), this);
- g_signal_connect(G_OBJECT(mpMenuBarWidget), "key-press-event", G_CALLBACK(MenuBarSignalKey), this);
-
- gtk_widget_show_all(mpMenuBarContainerWidget);
-
- ShowCloseButton( static_cast<MenuBar*>(mpVCLMenu.get())->HasCloseButton() );
-
- ApplyPersona();
-#else
- (void)mpMenuAllowShrinkWidget;
- (void)mpMenuBarContainerWidget;
-#endif
-}
-
-void GtkSalMenu::ApplyPersona()
-{
-#if GTK_CHECK_VERSION(3,0,0)
- if (!mpMenuBarContainerWidget)
- return;
- assert(mbMenuBar);
- // I'm dubious about the persona theming feature, but as it exists, lets try and support
- // it, apply the image to the mpMenuBarContainerWidget
- const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader();
-
- GtkStyleContext *pMenuBarContainerContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarContainerWidget));
- if (mpMenuBarContainerProvider)
- {
- gtk_style_context_remove_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider));
- mpMenuBarContainerProvider = nullptr;
- }
- GtkStyleContext *pMenuBarContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarWidget));
- if (mpMenuBarProvider)
- {
- gtk_style_context_remove_provider(pMenuBarContext, GTK_STYLE_PROVIDER(mpMenuBarProvider));
- mpMenuBarProvider = nullptr;
- }
-
- if (!rPersonaBitmap.IsEmpty())
- {
- if (maPersonaBitmap != rPersonaBitmap)
- {
- vcl::PNGWriter aPNGWriter(rPersonaBitmap);
- mxPersonaImage.reset(new utl::TempFile);
- mxPersonaImage->EnableKillingFile(true);
- SvStream* pStream = mxPersonaImage->GetStream(StreamMode::WRITE);
- aPNGWriter.Write(*pStream);
- mxPersonaImage->CloseStream();
- }
-
- mpMenuBarContainerProvider = gtk_css_provider_new();
- OUString aBuffer = "* { background-image: url(\"" + mxPersonaImage->GetURL() + "\"); background-position: top right; }";
- OString aResult = OUStringToOString(aBuffer, RTL_TEXTENCODING_UTF8);
- gtk_css_provider_load_from_data(mpMenuBarContainerProvider, aResult.getStr(), aResult.getLength(), nullptr);
- gtk_style_context_add_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
-
- // force the menubar to be transparent when persona is active otherwise for
- // me the menubar becomes gray when its in the backdrop
- mpMenuBarProvider = gtk_css_provider_new();
- static const gchar data[] = "* { "
- "background-image: none;"
- "background-color: transparent;"
- "}";
- gtk_css_provider_load_from_data(mpMenuBarProvider, data, -1, nullptr);
- gtk_style_context_add_provider(pMenuBarContext,
- GTK_STYLE_PROVIDER(mpMenuBarProvider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- }
- maPersonaBitmap = rPersonaBitmap;
-#else
- (void)maPersonaBitmap;
- (void)mpMenuBarContainerProvider;
- (void)mpMenuBarProvider;
-#endif
-}
-
-void GtkSalMenu::DestroyMenuBarWidget()
-{
-#if GTK_CHECK_VERSION(3,0,0)
- if (mpMenuBarContainerWidget)
- {
- gtk_widget_destroy(mpMenuBarContainerWidget);
- mpMenuBarContainerWidget = nullptr;
- mpCloseButton = nullptr;
- }
-#else
- (void)mpMenuBarContainerWidget;
-#endif
-}
-
-void GtkSalMenu::SetFrame(const SalFrame* pFrame)
-{
- SolarMutexGuard aGuard;
- assert(mbMenuBar);
- SAL_INFO("vcl.unity", "GtkSalMenu set to frame");
- 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
- mpFrame->SetMenu( this );
- mpFrame->EnsureAppMenuWatch();
-
- // Clean menu model and action group if needed.
- 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" ) );
- GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-action-group" ) );
- SAL_INFO("vcl.unity", "Found menu model: " << pMenuModel << " and action group: " << pActionGroup);
-
- if ( pMenuModel )
- {
- if ( g_menu_model_get_n_items( G_MENU_MODEL( pMenuModel ) ) > 0 )
- g_lo_menu_remove( pMenuModel, 0 );
-
- mpMenuModel = G_MENU_MODEL( g_lo_menu_new() );
- }
-
- if ( pActionGroup )
- {
- g_lo_action_group_clear( pActionGroup );
- mpActionGroup = G_ACTION_GROUP( pActionGroup );
- }
-
- // Generate the main menu structure.
- if ( PrepUpdate() )
- UpdateFull();
-
- g_lo_menu_insert_section( pMenuModel, 0, nullptr, mpMenuModel );
-
- if (!bUnityMode && static_cast<MenuBar*>(mpVCLMenu.get())->IsDisplayable())
- {
- DestroyMenuBarWidget();
- CreateMenuBarWidget();
- }
-}
-
-const GtkSalFrame* GtkSalMenu::GetFrame() const
-{
- SolarMutexGuard aGuard;
- const GtkSalMenu* pMenu = this;
- while( pMenu && ! pMenu->mpFrame )
- pMenu = pMenu->mpParentSalMenu;
- return pMenu ? pMenu->mpFrame : nullptr;
-}
-
-void GtkSalMenu::NativeCheckItem( unsigned nSection, unsigned nItemPos, MenuItemBits bits, gboolean bCheck )
-{
- SolarMutexGuard aGuard;
-
- if ( mpActionGroup == nullptr )
- return;
-
- gchar* aCommand = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
-
- if ( aCommand != nullptr || g_strcmp0( aCommand, "" ) != 0 )
- {
- GVariant *pCheckValue = nullptr;
- GVariant *pCurrentState = g_action_group_get_action_state( mpActionGroup, aCommand );
-
- if ( bits & MenuItemBits::RADIOCHECK )
- pCheckValue = bCheck ? g_variant_new_string( aCommand ) : g_variant_new_string( "" );
- else
- {
- // By default, all checked items are checkmark buttons.
- if (bCheck || pCurrentState != nullptr)
- pCheckValue = g_variant_new_boolean( bCheck );
- }
-
- if ( pCheckValue != nullptr )
- {
- if ( pCurrentState == nullptr || g_variant_equal( pCurrentState, pCheckValue ) == FALSE )
- {
- g_action_group_change_action_state( mpActionGroup, aCommand, pCheckValue );
- }
- else
- {
- g_variant_unref (pCheckValue);
- }
- }
-
- if ( pCurrentState != nullptr )
- g_variant_unref( pCurrentState );
- }
-
- if ( aCommand )
- g_free( aCommand );
-}
-
-void GtkSalMenu::NativeSetEnableItem( gchar const * aCommand, gboolean bEnable )
-{
- SolarMutexGuard aGuard;
- GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
-
- if ( g_action_group_get_action_enabled( G_ACTION_GROUP( pActionGroup ), aCommand ) != bEnable )
- g_lo_action_group_set_action_enabled( pActionGroup, aCommand, bEnable );
-}
-
-void GtkSalMenu::NativeSetItemText( unsigned nSection, unsigned nItemPos, const OUString& rText )
-{
- SolarMutexGuard aGuard;
- // Escape all underscores so that they don't get interpreted as hotkeys
- OUString aText = rText.replaceAll( "_", "__" );
- // Replace the LibreOffice hotkey identifier with an underscore
- aText = aText.replace( '~', '_' );
- OString aConvertedText = OUStringToOString( aText, RTL_TEXTENCODING_UTF8 );
-
- // Update item text only when necessary.
- gchar* aLabel = g_lo_menu_get_label_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
-
- if ( aLabel == nullptr || g_strcmp0( aLabel, aConvertedText.getStr() ) != 0 )
- g_lo_menu_set_label_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aConvertedText.getStr() );
-
- if ( aLabel )
- g_free( aLabel );
-}
-
-namespace
-{
- void DestroyMemoryStream(gpointer data)
- {
- SvMemoryStream* pMemStm = static_cast<SvMemoryStream*>(data);
- delete pMemStm;
- }
-}
-
-void GtkSalMenu::NativeSetItemIcon( unsigned nSection, unsigned nItemPos, const Image& rImage )
-{
-#if GLIB_CHECK_VERSION(2,38,0)
- if (!rImage && mbHasNullItemIcon)
- return;
-
- SolarMutexGuard aGuard;
-
- if (!!rImage)
- {
- SvMemoryStream* pMemStm = new SvMemoryStream;
- vcl::PNGWriter aWriter(rImage.GetBitmapEx());
- aWriter.Write(*pMemStm);
-
- GBytes *pBytes = g_bytes_new_with_free_func(pMemStm->GetData(),
- pMemStm->TellEnd(),
- DestroyMemoryStream,
- pMemStm);
-
- GIcon *pIcon = g_bytes_icon_new(pBytes);
-
- g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, pIcon );
- g_object_unref(pIcon);
- g_bytes_unref(pBytes);
- mbHasNullItemIcon = false;
- }
- else
- {
- g_lo_menu_set_icon_to_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos, nullptr );
- mbHasNullItemIcon = true;
- }
-#else
- (void)nSection;
- (void)nItemPos;
- (void)rImage;
-#endif
-}
-
-void GtkSalMenu::NativeSetAccelerator( unsigned nSection, unsigned nItemPos, const vcl::KeyCode& rKeyCode, const OUString& rKeyName )
-{
- SolarMutexGuard aGuard;
-
- if ( rKeyName.isEmpty() )
- return;
-
- guint nKeyCode;
- GdkModifierType nModifiers;
- GtkSalFrame::KeyCodeToGdkKey(rKeyCode, &nKeyCode, &nModifiers);
-
- gchar* aAccelerator = gtk_accelerator_name( nKeyCode, nModifiers );
-
- gchar* aCurrentAccel = g_lo_menu_get_accelerator_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItemPos );
-
- if ( aCurrentAccel == nullptr && g_strcmp0( aCurrentAccel, aAccelerator ) != 0 )
- g_lo_menu_set_accelerator_to_item_in_section ( G_LO_MENU( mpMenuModel ), nSection, nItemPos, aAccelerator );
-
- g_free( aAccelerator );
- g_free( aCurrentAccel );
-}
-
-bool GtkSalMenu::NativeSetItemCommand( unsigned nSection,
- unsigned nItemPos,
- sal_uInt16 nId,
- const gchar* aCommand,
- MenuItemBits nBits,
- bool bChecked,
- bool bIsSubmenu )
-{
- bool bSubMenuAddedOrRemoved = false;
-
- SolarMutexGuard aGuard;
- GLOActionGroup* pActionGroup = G_LO_ACTION_GROUP( mpActionGroup );
-
- GVariant *pTarget = nullptr;
-
- if (g_action_group_has_action(mpActionGroup, aCommand))
- g_lo_action_group_remove(pActionGroup, 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, 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 );
-
- // Menu item is not updated unless it's necessary.
- gchar* aCurrentCommand = g_lo_menu_get_command_from_item_in_section( pMenu, nSection, nItemPos );
-
- 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 it's 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_free(pLabel);
- }
-
- g_lo_menu_set_command_to_item_in_section( pMenu, nSection, nItemPos, aCommand );
-
- gchar* aItemCommand = g_strconcat("win.", aCommand, nullptr );
-
- if ( bIsSubmenu )
- g_lo_menu_set_submenu_action_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand );
- else
- {
- g_lo_menu_set_action_and_target_value_to_item_in_section( pMenu, nSection, nItemPos, aItemCommand, pTarget );
- pTarget = nullptr;
- }
-
- g_free( aItemCommand );
- }
-
- if ( aCurrentCommand )
- g_free( aCurrentCommand );
-
- if (pTarget)
- g_variant_unref(pTarget);
-
- return bSubMenuAddedOrRemoved;
-}
-
-GtkSalMenu* GtkSalMenu::GetTopLevel()
-{
- GtkSalMenu *pMenu = this;
- while (pMenu->mpParentSalMenu)
- pMenu = pMenu->mpParentSalMenu;
- return pMenu;
-}
-
-void GtkSalMenu::DispatchCommand(const gchar *pCommand)
-{
- SolarMutexGuard aGuard;
- MenuAndId aMenuAndId = decode_command(pCommand);
- GtkSalMenu* pSalSubMenu = aMenuAndId.first;
- GtkSalMenu* pTopLevel = pSalSubMenu->GetTopLevel();
- if (pTopLevel->mpMenuBarWidget)
- {
- // tdf#125803 spacebar will toggle radios and checkbuttons without automatically
- // closing the menu. To handle this properly I imagine we need to set groups for the
- // radiobuttons so the others visually untoggle when the active one is toggled and
- // we would further need to teach vcl that the state can change more than once.
- //
- // or we could unconditionally deactivate the menus if regardless of what particular
- // type of menu item got activated
- gtk_menu_shell_deactivate(GTK_MENU_SHELL(pTopLevel->mpMenuBarWidget));
- }
- pTopLevel->GetMenu()->HandleMenuCommandEvent(pSalSubMenu->GetMenu(), aMenuAndId.second);
-}
-
-void GtkSalMenu::ActivateAllSubmenus(Menu* pMenuBar)
-{
- for (GtkSalMenuItem* pSalItem : maItems)
- {
- if ( pSalItem->mpSubMenu != nullptr )
- {
- // We can re-enter this method via the new event loop that gets created
- // in GtkClipboardTransferable::getTransferDataFlavorsAsVector, so use the InActivateCallback
- // flag to detect that and skip some startup work.
- if (!pSalItem->mpSubMenu->mbInActivateCallback)
- {
- pSalItem->mpSubMenu->mbInActivateCallback = true;
- pMenuBar->HandleMenuActivateEvent(pSalItem->mpSubMenu->GetMenu());
- pSalItem->mpSubMenu->mbInActivateCallback = false;
- pSalItem->mpSubMenu->ActivateAllSubmenus(pMenuBar);
- pSalItem->mpSubMenu->Update();
- pMenuBar->HandleMenuDeActivateEvent(pSalItem->mpSubMenu->GetMenu());
- }
- }
- }
-}
-
-void GtkSalMenu::ClearActionGroupAndMenuModel()
-{
- SetMenuModel(nullptr);
- mpActionGroup = nullptr;
- for (GtkSalMenuItem* pSalItem : maItems)
- {
- if ( pSalItem->mpSubMenu != nullptr )
- {
- pSalItem->mpSubMenu->ClearActionGroupAndMenuModel();
- }
- }
-}
-
-void GtkSalMenu::Activate(const gchar* pCommand)
-{
- MenuAndId aMenuAndId = decode_command(pCommand);
- GtkSalMenu* pSalMenu = aMenuAndId.first;
- GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel();
- Menu* pVclMenu = pSalMenu->GetMenu();
- Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second);
- GtkSalMenu* pSubMenu = pSalMenu->GetItemAtPos(pVclMenu->GetItemPos(aMenuAndId.second))->mpSubMenu;
-
- pSubMenu->mbInActivateCallback = true;
- pTopLevel->GetMenu()->HandleMenuActivateEvent(pVclSubMenu);
- pSubMenu->mbInActivateCallback = false;
- pVclSubMenu->UpdateNativeMenu();
-}
-
-void GtkSalMenu::Deactivate(const gchar* pCommand)
-{
- MenuAndId aMenuAndId = decode_command(pCommand);
- GtkSalMenu* pSalMenu = aMenuAndId.first;
- GtkSalMenu* pTopLevel = pSalMenu->GetTopLevel();
- Menu* pVclMenu = pSalMenu->GetMenu();
- Menu* pVclSubMenu = pVclMenu->GetPopupMenu(aMenuAndId.second);
- pTopLevel->GetMenu()->HandleMenuDeActivateEvent(pVclSubMenu);
-}
-
-void GtkSalMenu::EnableUnity(bool bEnable)
-{
- bUnityMode = bEnable;
-
- MenuBar* pMenuBar(static_cast<MenuBar*>(mpVCLMenu.get()));
- bool bDisplayable(pMenuBar->IsDisplayable());
-
- if (bEnable)
- {
- DestroyMenuBarWidget();
- UpdateFull();
- if (!bDisplayable)
- ShowMenuBar(false);
- }
- else
- {
- Update();
- ShowMenuBar(bDisplayable);
- }
-
- pMenuBar->LayoutChanged();
-}
-
-void GtkSalMenu::ShowMenuBar( bool bVisible )
-{
- // Unity tdf#106271: Can't hide global menu, so empty it instead when user wants to hide menubar,
- if (bUnityMode)
- {
- if (bVisible)
- Update();
- else if (mpMenuModel && g_menu_model_get_n_items(G_MENU_MODEL(mpMenuModel)) > 0)
- g_lo_menu_remove(G_LO_MENU(mpMenuModel), 0);
- }
- else if (bVisible)
- CreateMenuBarWidget();
- else
- DestroyMenuBarWidget();
-}
-
-bool GtkSalMenu::IsItemVisible( unsigned nPos )
-{
- SolarMutexGuard aGuard;
- bool bVisible = false;
-
- if ( nPos < maItems.size() )
- bVisible = maItems[ nPos ]->mbVisible;
-
- return bVisible;
-}
-
-void GtkSalMenu::CheckItem( unsigned, bool )
-{
-}
-
-void GtkSalMenu::EnableItem( unsigned nPos, bool bEnable )
-{
- SolarMutexGuard aGuard;
- if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) )
- {
- gchar* pCommand = GetCommandForItem( GetItemAtPos( nPos ) );
- NativeSetEnableItem( pCommand, bEnable );
- g_free( pCommand );
- }
-}
-
-void GtkSalMenu::ShowItem( unsigned nPos, bool bShow )
-{
- SolarMutexGuard aGuard;
- if ( nPos < maItems.size() )
- {
- maItems[ nPos ]->mbVisible = bShow;
- if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar )
- Update();
- }
-}
-
-void GtkSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const OUString& rText )
-{
- SolarMutexGuard aGuard;
- if ( bUnityMode && !mbInActivateCallback && !mbNeedsUpdate && GetTopLevel()->mbMenuBar && ( nPos < maItems.size() ) )
- {
- gchar* pCommand = GetCommandForItem( static_cast< GtkSalMenuItem* >( pSalMenuItem ) );
-
- gint nSectionsCount = g_menu_model_get_n_items( mpMenuModel );
- for ( gint nSection = 0; nSection < nSectionsCount; ++nSection )
- {
- gint nItemsCount = g_lo_menu_get_n_items_from_section( G_LO_MENU( mpMenuModel ), nSection );
- for ( gint nItem = 0; nItem < nItemsCount; ++nItem )
- {
- gchar* pCommandFromModel = g_lo_menu_get_command_from_item_in_section( G_LO_MENU( mpMenuModel ), nSection, nItem );
-
- if ( !g_strcmp0( pCommandFromModel, pCommand ) )
- {
- NativeSetItemText( nSection, nItem, rText );
- g_free( pCommandFromModel );
- g_free( pCommand );
- return;
- }
-
- g_free( pCommandFromModel );
- }
- }
-
- g_free( pCommand );
- }
-}
-
-void GtkSalMenu::SetItemImage( unsigned, SalMenuItem*, const Image& )
-{
-}
-
-void GtkSalMenu::SetAccelerator( unsigned, SalMenuItem*, const vcl::KeyCode&, const OUString& )
-{
-}
-
-void GtkSalMenu::GetSystemMenuData( SystemMenuData* )
-{
-}
-
-int GtkSalMenu::GetMenuBarHeight() const
-{
-#if GTK_CHECK_VERSION(3,0,0)
- return mpMenuBarWidget ? gtk_widget_get_allocated_height(mpMenuBarWidget) : 0;
-#else
- return 0;
-#endif
-}
-
-/*
- * GtkSalMenuItem
- */
-
-GtkSalMenuItem::GtkSalMenuItem( const SalItemParams* pItemData ) :
- mpParentMenu( nullptr ),
- mpSubMenu( nullptr ),
- mnType( pItemData->eType ),
- mnId( pItemData->nId ),
- mbVisible( true )
-{
-}
-
-GtkSalMenuItem::~GtkSalMenuItem()
-{
-}
-
-#endif
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */