summaryrefslogtreecommitdiffstats
path: root/framework/source/fwe/classes/addonmenu.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'framework/source/fwe/classes/addonmenu.cxx')
-rw-r--r--framework/source/fwe/classes/addonmenu.cxx299
1 files changed, 299 insertions, 0 deletions
diff --git a/framework/source/fwe/classes/addonmenu.cxx b/framework/source/fwe/classes/addonmenu.cxx
new file mode 100644
index 000000000..423a157f8
--- /dev/null
+++ b/framework/source/fwe/classes/addonmenu.cxx
@@ -0,0 +1,299 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <addonmenu.hxx>
+#include <framework/addonsoptions.hxx>
+#include <menuconfiguration.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/menu.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+
+namespace framework
+{
+
+bool AddonMenuManager::HasAddonMenuElements()
+{
+ return AddonsOptions().HasAddonsMenu();
+}
+
+// Create the Add-Ons menu
+VclPtr<PopupMenu> AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame )
+{
+ AddonsOptions aOptions;
+ VclPtr<PopupMenu> pAddonMenu;
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
+ if ( rAddonMenuEntries.hasElements() )
+ {
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ pAddonMenu = VclPtr<PopupMenu>::Create();
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier( rFrame );
+ AddonMenuManager::BuildMenu( pAddonMenu, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
+
+ // Don't return an empty Add-On menu
+ if ( pAddonMenu->GetItemCount() == 0 )
+ {
+ pAddonMenu.disposeAndClear();
+ }
+ }
+
+ return pAddonMenu;
+}
+
+// Returns the next insert position from nPos.
+sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
+{
+ return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
+}
+
+static sal_uInt16 FindMenuId( Menu const * pMenu, std::u16string_view aCommand )
+{
+ sal_uInt16 nPos = 0;
+ OUString aCmd;
+ for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nId = pMenu->GetItemId( nPos );
+ aCmd = pMenu->GetItemCommand( nId );
+ if ( aCmd == aCommand )
+ return nId;
+ }
+
+ return USHRT_MAX;
+}
+
+// Merge the Add-Ons help menu items into the given menu bar at a defined pos
+void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
+ MenuBar const * pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ PopupMenu* pHelpMenu(nullptr);
+ sal_uInt16 nId = FindMenuId(pMergeMenuBar, u".uno:HelpMenu");
+ if ( nId != USHRT_MAX )
+ pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
+
+ if ( !pHelpMenu )
+ return;
+
+ // Add-Ons help menu items should be inserted after the "registration" menu item
+ sal_uInt16 nItemCount = pHelpMenu->GetItemCount();
+ sal_uInt16 nInsSepAfterPos = MENU_APPEND;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ AddonsOptions aOptions;
+
+ // try to detect the about menu item with the command URL
+ nId = FindMenuId(pHelpMenu, u".uno:About");
+ sal_uInt16 nInsPos = pHelpMenu->GetItemPos( nId );
+
+ const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
+
+ if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MenuItemType::SEPARATOR )
+ nInsSepAfterPos = nInsPos;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+ AddonMenuManager::BuildMenu( pHelpMenu, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
+
+ if ( pHelpMenu->GetItemCount() > nItemCount )
+ {
+ if ( nInsSepAfterPos < MENU_APPEND )
+ {
+ nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
+ if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MenuItemType::SEPARATOR )
+ pHelpMenu->InsertSeparator(OString(), nInsSepAfterPos);
+ }
+ pHelpMenu->InsertSeparator(OString(), nItemCount);
+ }
+}
+
+// Merge the addon popup menus into the given menu bar at the provided pos.
+void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
+ sal_uInt16 nMergeAtPos,
+ MenuBar* pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ AddonsOptions aAddonsOptions;
+ sal_uInt16 nInsertPos = nMergeAtPos;
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
+ for ( const Sequence< PropertyValue >& rEntry : rAddonMenuEntries )
+ {
+ AddonMenuManager::GetMenuEntry( rEntry,
+ aTitle,
+ aURL,
+ aTarget,
+ aContext,
+ aAddonSubMenu );
+ if ( !aTitle.isEmpty() &&
+ !aURL.isEmpty() &&
+ aAddonSubMenu.hasElements() &&
+ AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
+ {
+ sal_uInt16 nId = nUniqueMenuId++;
+ VclPtrInstance<PopupMenu> pAddonPopupMenu;
+
+ AddonMenuManager::BuildMenu( pAddonPopupMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
+
+ if ( pAddonPopupMenu->GetItemCount() > 0 )
+ {
+ pMergeMenuBar->InsertItem( nId, aTitle, MenuItemBits::NONE, OString(), nInsertPos++ );
+ pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
+
+ // Store the command URL into the VCL menu bar for later identification
+ pMergeMenuBar->SetItemCommand( nId, aURL );
+ }
+ else
+ pAddonPopupMenu.disposeAndClear();
+ }
+ }
+}
+
+// Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
+void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu,
+ sal_uInt16 nInsPos,
+ sal_uInt16& nUniqueMenuId,
+ const Sequence< Sequence< PropertyValue > >& aAddonMenuDefinition,
+ const Reference< XFrame >& rFrame,
+ const OUString& rModuleIdentifier )
+{
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ bool bInsertSeparator = false;
+ sal_uInt32 i = 0;
+ sal_uInt32 nElements = 0;
+ sal_uInt32 nCount = aAddonMenuDefinition.getLength();
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+
+ for ( i = 0; i < nCount; ++i )
+ {
+ GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aContext, aAddonSubMenu );
+
+ if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( aTitle.isEmpty() && aURL.isEmpty() ))
+ continue;
+
+ if ( aURL == "private:separator" )
+ bInsertSeparator = true;
+ else
+ {
+ VclPtr<PopupMenu> pSubMenu;
+ if ( aAddonSubMenu.hasElements() )
+ {
+ pSubMenu = VclPtr<PopupMenu>::Create();
+ AddonMenuManager::BuildMenu( pSubMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
+
+ // Don't create a menu item for an empty sub menu
+ if ( pSubMenu->GetItemCount() == 0 )
+ {
+ pSubMenu.disposeAndClear();
+ continue;
+ }
+ }
+
+ if ( bInsertSeparator && nElements > 0 )
+ {
+ // Insert a separator only when we insert a new element afterwards and we
+ // have already one before us
+ nElements = 0;
+ bInsertSeparator = false;
+ pCurrentMenu->InsertSeparator(OString(), nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+ }
+
+ sal_uInt16 nId = nUniqueMenuId++;
+ pCurrentMenu->InsertItem(nId, aTitle, MenuItemBits::NONE, OString(), nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+
+ ++nElements;
+
+ void* nAttributePtr = MenuAttributes::CreateAttribute(aTarget, OUString());
+ pCurrentMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute);
+ pCurrentMenu->SetItemCommand( nId, aURL );
+
+ if ( pSubMenu )
+ pCurrentMenu->SetPopupMenu( nId, pSubMenu );
+ }
+ }
+}
+
+// Retrieve the menu entry property values from a sequence
+void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
+ OUString& rTitle,
+ OUString& rURL,
+ OUString& rTarget,
+ OUString& rContext,
+ Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
+{
+ // Reset submenu parameter
+ rAddonSubMenu = Sequence< Sequence< PropertyValue > >();
+
+ for ( const PropertyValue& rEntry : rAddonMenuEntry )
+ {
+ OUString aMenuEntryPropName = rEntry.Name;
+ if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL )
+ rEntry.Value >>= rURL;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE )
+ rEntry.Value >>= rTitle;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TARGET )
+ rEntry.Value >>= rTarget;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU )
+ rEntry.Value >>= rAddonSubMenu;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
+ rEntry.Value >>= rContext;
+ }
+}
+
+// Check if the context string matches the provided xModel context
+bool AddonMenuManager::IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view rContext )
+{
+ if ( rContext.empty() )
+ return true;
+
+ if ( !rModuleIdentifier.empty() )
+ {
+ return rContext.find( rModuleIdentifier ) != std::u16string_view::npos;
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */