1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <addonmenu.hxx>
21 #include <framework/addonsoptions.hxx>
22 #include <menuconfiguration.hxx>
24 #include <com/sun/star/uno/Reference.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <com/sun/star/beans/PropertyValue.hpp>
28 #include <vcl/commandinfoprovider.hxx>
29 #include <vcl/menu.hxx>
31 using namespace ::com::sun::star::uno
;
32 using namespace ::com::sun::star::frame
;
33 using namespace ::com::sun::star::beans
;
38 bool AddonMenuManager::HasAddonMenuElements()
40 return AddonsOptions().HasAddonsMenu();
43 // Create the Add-Ons menu
44 VclPtr
<PopupMenu
> AddonMenuManager::CreateAddonMenu( const Reference
< XFrame
>& rFrame
)
46 AddonsOptions aOptions
;
47 VclPtr
<PopupMenu
> pAddonMenu
;
49 const Sequence
< Sequence
< PropertyValue
> >& rAddonMenuEntries
= aOptions
.GetAddonsMenu();
50 if ( rAddonMenuEntries
.hasElements() )
52 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
53 pAddonMenu
= VclPtr
<PopupMenu
>::Create();
54 OUString aModuleIdentifier
= vcl::CommandInfoProvider::GetModuleIdentifier( rFrame
);
55 AddonMenuManager::BuildMenu( pAddonMenu
, MENU_APPEND
, nUniqueMenuId
, rAddonMenuEntries
, rFrame
, aModuleIdentifier
);
57 // Don't return an empty Add-On menu
58 if ( pAddonMenu
->GetItemCount() == 0 )
60 pAddonMenu
.disposeAndClear();
67 // Returns the next insert position from nPos.
68 sal_uInt16
AddonMenuManager::GetNextPos( sal_uInt16 nPos
)
70 return ( nPos
== MENU_APPEND
) ? MENU_APPEND
: ( nPos
+1 );
73 static sal_uInt16
FindMenuId( Menu
const * pMenu
, std::u16string_view aCommand
)
77 for ( nPos
= 0; nPos
< pMenu
->GetItemCount(); nPos
++ )
79 sal_uInt16 nId
= pMenu
->GetItemId( nPos
);
80 aCmd
= pMenu
->GetItemCommand( nId
);
81 if ( aCmd
== aCommand
)
88 // Merge the Add-Ons help menu items into the given menu bar at a defined pos
89 void AddonMenuManager::MergeAddonHelpMenu( const Reference
< XFrame
>& rFrame
,
90 MenuBar
const * pMergeMenuBar
)
95 PopupMenu
* pHelpMenu(nullptr);
96 sal_uInt16 nId
= FindMenuId(pMergeMenuBar
, u
".uno:HelpMenu");
97 if ( nId
!= USHRT_MAX
)
98 pHelpMenu
= pMergeMenuBar
->GetPopupMenu( nId
);
103 // Add-Ons help menu items should be inserted after the "registration" menu item
104 sal_uInt16 nItemCount
= pHelpMenu
->GetItemCount();
105 sal_uInt16 nInsSepAfterPos
= MENU_APPEND
;
106 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
107 AddonsOptions aOptions
;
109 // try to detect the about menu item with the command URL
110 nId
= FindMenuId(pHelpMenu
, u
".uno:About");
111 sal_uInt16 nInsPos
= pHelpMenu
->GetItemPos( nId
);
113 const Sequence
< Sequence
< PropertyValue
> >& rAddonHelpMenuEntries
= aOptions
.GetAddonsHelpMenu();
115 if ( nInsPos
< nItemCount
&& pHelpMenu
->GetItemType( nInsPos
) != MenuItemType::SEPARATOR
)
116 nInsSepAfterPos
= nInsPos
;
118 OUString aModuleIdentifier
= vcl::CommandInfoProvider::GetModuleIdentifier(rFrame
);
119 AddonMenuManager::BuildMenu( pHelpMenu
, nInsPos
, nUniqueMenuId
, rAddonHelpMenuEntries
, rFrame
, aModuleIdentifier
);
121 if ( pHelpMenu
->GetItemCount() > nItemCount
)
123 if ( nInsSepAfterPos
< MENU_APPEND
)
125 nInsSepAfterPos
+= ( pHelpMenu
->GetItemCount() - nItemCount
);
126 if ( pHelpMenu
->GetItemType( nInsSepAfterPos
) != MenuItemType::SEPARATOR
)
127 pHelpMenu
->InsertSeparator({}, nInsSepAfterPos
);
129 pHelpMenu
->InsertSeparator({}, nItemCount
);
133 // Merge the addon popup menus into the given menu bar at the provided pos.
134 void AddonMenuManager::MergeAddonPopupMenus( const Reference
< XFrame
>& rFrame
,
135 sal_uInt16 nMergeAtPos
,
136 MenuBar
* pMergeMenuBar
)
138 if ( !pMergeMenuBar
)
141 AddonsOptions aAddonsOptions
;
142 sal_uInt16 nInsertPos
= nMergeAtPos
;
148 Sequence
< Sequence
< PropertyValue
> > aAddonSubMenu
;
149 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
151 OUString aModuleIdentifier
= vcl::CommandInfoProvider::GetModuleIdentifier(rFrame
);
153 const Sequence
< Sequence
< PropertyValue
> >& rAddonMenuEntries
= aAddonsOptions
.GetAddonsMenuBarPart();
154 for ( const Sequence
< PropertyValue
>& rEntry
: rAddonMenuEntries
)
156 AddonMenuManager::GetMenuEntry( rEntry
,
162 if ( !aTitle
.isEmpty() &&
164 aAddonSubMenu
.hasElements() &&
165 AddonMenuManager::IsCorrectContext( aModuleIdentifier
, aContext
))
167 sal_uInt16 nId
= nUniqueMenuId
++;
168 VclPtrInstance
<PopupMenu
> pAddonPopupMenu
;
170 AddonMenuManager::BuildMenu( pAddonPopupMenu
, MENU_APPEND
, nUniqueMenuId
, aAddonSubMenu
, rFrame
, aModuleIdentifier
);
172 if ( pAddonPopupMenu
->GetItemCount() > 0 )
174 pMergeMenuBar
->InsertItem( nId
, aTitle
, MenuItemBits::NONE
, {}, nInsertPos
++);
175 pMergeMenuBar
->SetPopupMenu( nId
, pAddonPopupMenu
);
177 // Store the command URL into the VCL menu bar for later identification
178 pMergeMenuBar
->SetItemCommand( nId
, aURL
);
181 pAddonPopupMenu
.disposeAndClear();
186 // Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
187 void AddonMenuManager::BuildMenu( PopupMenu
* pCurrentMenu
,
189 sal_uInt16
& nUniqueMenuId
,
190 const Sequence
< Sequence
< PropertyValue
> >& aAddonMenuDefinition
,
191 const Reference
< XFrame
>& rFrame
,
192 const OUString
& rModuleIdentifier
)
194 Sequence
< Sequence
< PropertyValue
> > aAddonSubMenu
;
195 bool bInsertSeparator
= false;
197 sal_uInt32 nElements
= 0;
198 sal_uInt32 nCount
= aAddonMenuDefinition
.getLength();
205 for ( i
= 0; i
< nCount
; ++i
)
207 GetMenuEntry( aAddonMenuDefinition
[i
], aTitle
, aURL
, aTarget
, aContext
, aAddonSubMenu
);
209 if ( !IsCorrectContext( rModuleIdentifier
, aContext
) || ( aTitle
.isEmpty() && aURL
.isEmpty() ))
212 if ( aURL
== "private:separator" )
213 bInsertSeparator
= true;
216 VclPtr
<PopupMenu
> pSubMenu
;
217 if ( aAddonSubMenu
.hasElements() )
219 pSubMenu
= VclPtr
<PopupMenu
>::Create();
220 AddonMenuManager::BuildMenu( pSubMenu
, MENU_APPEND
, nUniqueMenuId
, aAddonSubMenu
, rFrame
, rModuleIdentifier
);
222 // Don't create a menu item for an empty sub menu
223 if ( pSubMenu
->GetItemCount() == 0 )
225 pSubMenu
.disposeAndClear();
230 if ( bInsertSeparator
&& nElements
> 0 )
232 // Insert a separator only when we insert a new element afterwards and we
233 // have already one before us
235 bInsertSeparator
= false;
236 pCurrentMenu
->InsertSeparator({}, nInsPos
);
237 nInsPos
= AddonMenuManager::GetNextPos( nInsPos
);
240 sal_uInt16 nId
= nUniqueMenuId
++;
241 pCurrentMenu
->InsertItem(nId
, aTitle
, MenuItemBits::NONE
, {}, nInsPos
);
242 nInsPos
= AddonMenuManager::GetNextPos( nInsPos
);
246 void* nAttributePtr
= MenuAttributes::CreateAttribute(aTarget
, OUString());
247 pCurrentMenu
->SetUserValue(nId
, nAttributePtr
, MenuAttributes::ReleaseAttribute
);
248 pCurrentMenu
->SetItemCommand( nId
, aURL
);
251 pCurrentMenu
->SetPopupMenu( nId
, pSubMenu
);
256 // Retrieve the menu entry property values from a sequence
257 void AddonMenuManager::GetMenuEntry( const Sequence
< PropertyValue
>& rAddonMenuEntry
,
262 Sequence
< Sequence
< PropertyValue
> >& rAddonSubMenu
)
264 // Reset submenu parameter
265 rAddonSubMenu
= Sequence
< Sequence
< PropertyValue
> >();
267 for ( const PropertyValue
& rEntry
: rAddonMenuEntry
)
269 OUString aMenuEntryPropName
= rEntry
.Name
;
270 if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_URL
)
271 rEntry
.Value
>>= rURL
;
272 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_TITLE
)
273 rEntry
.Value
>>= rTitle
;
274 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_TARGET
)
275 rEntry
.Value
>>= rTarget
;
276 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_SUBMENU
)
277 rEntry
.Value
>>= rAddonSubMenu
;
278 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_CONTEXT
)
279 rEntry
.Value
>>= rContext
;
283 // Check if the context string matches the provided xModel context
284 bool AddonMenuManager::IsCorrectContext( std::u16string_view rModuleIdentifier
, std::u16string_view rContext
)
286 if ( rContext
.empty() )
289 if ( !rModuleIdentifier
.empty() )
291 return rContext
.find( rModuleIdentifier
) != std::u16string_view::npos
;
299 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */