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 <framework/addonmenu.hxx>
21 #include <framework/addonsoptions.hxx>
23 #include <framework/imageproducer.hxx>
24 #include <framework/menuconfiguration.hxx>
27 #include <com/sun/star/uno/Reference.hxx>
28 #include <com/sun/star/util/URL.hpp>
29 #include <com/sun/star/util/XURLTransformer.hpp>
30 #include <com/sun/star/frame/ModuleManager.hpp>
31 #include <com/sun/star/frame/XModuleManager.hpp>
33 #include <tools/config.hxx>
34 #include <vcl/svapp.hxx>
35 #include <svtools/menuoptions.hxx>
36 #include <svl/solar.hrc>
38 using namespace ::com::sun::star::uno
;
39 using namespace ::com::sun::star::lang
;
40 using namespace ::com::sun::star::frame
;
41 using namespace ::com::sun::star::beans
;
43 // Please look at sfx2/inc/sfxsids.hrc the values are defined there. Due to build dependencies
44 // we cannot include the header file.
45 const sal_uInt16 SID_HELPMENU
= (SID_SFX_START
+ 410);
50 AddonMenu::AddonMenu( const ::com::sun::star::uno::Reference
< ::com::sun::star::frame::XFrame
>& rFrame
) :
55 AddonMenu::~AddonMenu()
57 for ( sal_uInt16 i
= 0; i
< GetItemCount(); i
++ )
59 if ( GetItemType( i
) != MenuItemType::SEPARATOR
)
61 sal_uInt16 nId
= GetItemId( i
);
62 delete GetPopupMenu( nId
);
67 // Check if command URL string has the unique prefix to identify addon popup menus
68 bool AddonPopupMenu::IsCommandURLPrefix( const OUString
& aCmdURL
)
70 return aCmdURL
.startsWith( ADDONSPOPUPMENU_URL_PREFIX_STR
);
73 AddonPopupMenu::AddonPopupMenu( const com::sun::star::uno::Reference
< com::sun::star::frame::XFrame
>& rFrame
) :
78 AddonPopupMenu::~AddonPopupMenu()
82 static OUString
GetModuleIdentifier(const Reference
<XComponentContext
>& rContext
,
83 const Reference
< XFrame
>& rFrame
)
85 Reference
< XModuleManager
> xModuleManager(ModuleManager::create(rContext
));
86 if ( xModuleManager
.is() )
90 return xModuleManager
->identify( rFrame
);
99 bool AddonMenuManager::HasAddonMenuElements()
101 return AddonsOptions().HasAddonsMenu();
104 // Factory method to create different Add-On menu types
105 PopupMenu
* AddonMenuManager::CreatePopupMenuType( MenuType eMenuType
, const Reference
< XFrame
>& rFrame
)
107 if ( eMenuType
== ADDON_MENU
)
108 return new AddonMenu( rFrame
);
109 else if ( eMenuType
== ADDON_POPUPMENU
)
110 return new AddonPopupMenu( rFrame
);
115 // Create the Add-Ons menu
116 AddonMenu
* AddonMenuManager::CreateAddonMenu( const Reference
< XFrame
>& rFrame
,
117 const Reference
< XComponentContext
>& rContext
)
119 AddonsOptions aOptions
;
120 AddonMenu
* pAddonMenu
= NULL
;
121 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
123 const Sequence
< Sequence
< PropertyValue
> >& rAddonMenuEntries
= aOptions
.GetAddonsMenu();
124 if ( rAddonMenuEntries
.getLength() > 0 )
126 pAddonMenu
= static_cast<AddonMenu
*>(AddonMenuManager::CreatePopupMenuType( ADDON_MENU
, rFrame
));
127 ::rtl::OUString aModuleIdentifier
= GetModuleIdentifier( rContext
, rFrame
);
128 AddonMenuManager::BuildMenu( pAddonMenu
, ADDON_MENU
, MENU_APPEND
, nUniqueMenuId
, rAddonMenuEntries
, rFrame
, aModuleIdentifier
);
130 // Don't return an empty Add-On menu
131 if ( pAddonMenu
->GetItemCount() == 0 )
141 // Returns the next insert position from nPos.
142 sal_uInt16
AddonMenuManager::GetNextPos( sal_uInt16 nPos
)
144 return ( nPos
== MENU_APPEND
) ? MENU_APPEND
: ( nPos
+1 );
147 static sal_uInt16
FindMenuId( Menu
* pMenu
, const OUString
& aCommand
)
151 for ( nPos
= 0; nPos
< pMenu
->GetItemCount(); nPos
++ )
153 sal_uInt16 nId
= pMenu
->GetItemId( nPos
);
154 aCmd
= pMenu
->GetItemCommand( nId
);
155 if ( aCmd
== aCommand
)
162 // Merge the Add-Ons help menu items into the given menu bar at a defined pos
163 void AddonMenuManager::MergeAddonHelpMenu( const Reference
< XFrame
>& rFrame
,
164 MenuBar
* pMergeMenuBar
,
165 const Reference
<XComponentContext
>& rContext
)
169 PopupMenu
* pHelpMenu
= pMergeMenuBar
->GetPopupMenu( SID_HELPMENU
);
172 sal_uInt16 nId
= FindMenuId(pMergeMenuBar
, OUString(".uno:HelpMenu"));
173 if ( nId
!= USHRT_MAX
)
174 pHelpMenu
= pMergeMenuBar
->GetPopupMenu( nId
);
179 // Add-Ons help menu items should be inserted after the "registration" menu item
180 sal_uInt16 nItemCount
= pHelpMenu
->GetItemCount();
181 sal_uInt16 nInsSepAfterPos
= MENU_APPEND
;
182 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
183 AddonsOptions aOptions
;
185 // try to detect the about menu item with the command URL
186 sal_uInt16 nId
= FindMenuId(pHelpMenu
, OUString(".uno:About"));
187 sal_uInt16 nInsPos
= pHelpMenu
->GetItemPos( nId
);
189 const Sequence
< Sequence
< PropertyValue
> >& rAddonHelpMenuEntries
= aOptions
.GetAddonsHelpMenu();
191 if ( nInsPos
< nItemCount
&& pHelpMenu
->GetItemType( nInsPos
) != MenuItemType::SEPARATOR
)
192 nInsSepAfterPos
= nInsPos
;
194 ::rtl::OUString aModuleIdentifier
= GetModuleIdentifier(rContext
, rFrame
);
195 AddonMenuManager::BuildMenu( pHelpMenu
, ADDON_MENU
, nInsPos
, nUniqueMenuId
, rAddonHelpMenuEntries
, rFrame
, aModuleIdentifier
);
197 if ( pHelpMenu
->GetItemCount() > nItemCount
)
199 if ( nInsSepAfterPos
< MENU_APPEND
)
201 nInsSepAfterPos
+= ( pHelpMenu
->GetItemCount() - nItemCount
);
202 if ( pHelpMenu
->GetItemType( nInsSepAfterPos
) != MenuItemType::SEPARATOR
)
203 pHelpMenu
->InsertSeparator(OString(), nInsSepAfterPos
);
205 pHelpMenu
->InsertSeparator(OString(), nItemCount
);
211 // Merge the addon popup menus into the given menu bar at the provided pos.
212 void AddonMenuManager::MergeAddonPopupMenus( const Reference
< XFrame
>& rFrame
,
213 sal_uInt16 nMergeAtPos
,
214 MenuBar
* pMergeMenuBar
,
215 const Reference
< XComponentContext
>& rContext
)
219 AddonsOptions aAddonsOptions
;
220 sal_uInt16 nInsertPos
= nMergeAtPos
;
227 Sequence
< Sequence
< PropertyValue
> > aAddonSubMenu
;
228 sal_uInt16 nUniqueMenuId
= ADDONMENU_ITEMID_START
;
229 OUString aModuleIdentifier
= GetModuleIdentifier(rContext
, rFrame
);
231 const Sequence
< Sequence
< PropertyValue
> >& rAddonMenuEntries
= aAddonsOptions
.GetAddonsMenuBarPart();
232 for ( sal_Int32 i
= 0; i
< rAddonMenuEntries
.getLength(); i
++ )
234 AddonMenuManager::GetMenuEntry( rAddonMenuEntries
[i
],
241 if ( !aTitle
.isEmpty() &&
243 aAddonSubMenu
.getLength() > 0 &&
244 AddonMenuManager::IsCorrectContext( aModuleIdentifier
, aContext
))
246 sal_uInt16 nId
= nUniqueMenuId
++;
247 AddonPopupMenu
* pAddonPopupMenu
= static_cast<AddonPopupMenu
*>(AddonMenuManager::CreatePopupMenuType( ADDON_POPUPMENU
, rFrame
));
249 AddonMenuManager::BuildMenu( pAddonPopupMenu
, ADDON_MENU
, MENU_APPEND
, nUniqueMenuId
, aAddonSubMenu
, rFrame
, aModuleIdentifier
);
251 if ( pAddonPopupMenu
->GetItemCount() > 0 )
253 pAddonPopupMenu
->SetCommandURL( aURL
);
254 pMergeMenuBar
->InsertItem( nId
, aTitle
, MenuItemBits::NONE
, OString(), nInsertPos
++ );
255 pMergeMenuBar
->SetPopupMenu( nId
, pAddonPopupMenu
);
257 // Store the command URL into the VCL menu bar for later identification
258 pMergeMenuBar
->SetItemCommand( nId
, aURL
);
261 delete pAddonPopupMenu
;
267 // Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
268 void AddonMenuManager::BuildMenu( PopupMenu
* pCurrentMenu
,
269 MenuType nSubMenuType
,
271 sal_uInt16
& nUniqueMenuId
,
272 const Sequence
< Sequence
< PropertyValue
> >& aAddonMenuDefinition
,
273 const Reference
< XFrame
>& rFrame
,
274 const ::rtl::OUString
& rModuleIdentifier
)
276 Sequence
< Sequence
< PropertyValue
> > aAddonSubMenu
;
277 bool bInsertSeparator
= false;
279 sal_uInt32 nElements
= 0;
280 sal_uInt32 nCount
= aAddonMenuDefinition
.getLength();
281 AddonsOptions aAddonsOptions
;
289 for ( i
= 0; i
< nCount
; ++i
)
291 GetMenuEntry( aAddonMenuDefinition
[i
], aTitle
, aURL
, aTarget
, aImageId
, aContext
, aAddonSubMenu
);
293 if ( !IsCorrectContext( rModuleIdentifier
, aContext
) || ( aTitle
.isEmpty() && aURL
.isEmpty() ))
296 if ( aURL
== "private:separator" )
297 bInsertSeparator
= true;
300 PopupMenu
* pSubMenu
= NULL
;
301 if ( aAddonSubMenu
.getLength() > 0 )
303 pSubMenu
= AddonMenuManager::CreatePopupMenuType( nSubMenuType
, rFrame
);
304 AddonMenuManager::BuildMenu( pSubMenu
, nSubMenuType
, MENU_APPEND
, nUniqueMenuId
, aAddonSubMenu
, rFrame
, rModuleIdentifier
);
306 // Don't create a menu item for an empty sub menu
307 if ( pSubMenu
->GetItemCount() == 0 )
315 if ( bInsertSeparator
&& nElements
> 0 )
317 // Insert a separator only when we insert a new element afterwards and we
318 // have already one before us
320 bInsertSeparator
= false;
321 pCurrentMenu
->InsertSeparator(OString(), nInsPos
);
322 nInsPos
= AddonMenuManager::GetNextPos( nInsPos
);
325 sal_uInt16 nId
= nUniqueMenuId
++;
326 pCurrentMenu
->InsertItem(nId
, aTitle
, MenuItemBits::NONE
, OString(), nInsPos
);
327 nInsPos
= AddonMenuManager::GetNextPos( nInsPos
);
331 // Store values from configuration to the New and Wizard menu entries to enable
332 // sfx2 based code to support high contrast mode correctly!
333 sal_uIntPtr nAttributePtr
= MenuAttributes::CreateAttribute(aTarget
, aImageId
);
334 pCurrentMenu
->SetUserValue(nId
, nAttributePtr
, MenuAttributes::ReleaseAttribute
);
335 pCurrentMenu
->SetItemCommand( nId
, aURL
);
338 pCurrentMenu
->SetPopupMenu( nId
, pSubMenu
);
343 // Retrieve the menu entry property values from a sequence
344 void AddonMenuManager::GetMenuEntry( const Sequence
< PropertyValue
>& rAddonMenuEntry
,
350 Sequence
< Sequence
< PropertyValue
> >& rAddonSubMenu
)
352 // Reset submenu parameter
353 rAddonSubMenu
= Sequence
< Sequence
< PropertyValue
> >();
355 for ( int i
= 0; i
< rAddonMenuEntry
.getLength(); i
++ )
357 OUString aMenuEntryPropName
= rAddonMenuEntry
[i
].Name
;
358 if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_URL
)
359 rAddonMenuEntry
[i
].Value
>>= rURL
;
360 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_TITLE
)
361 rAddonMenuEntry
[i
].Value
>>= rTitle
;
362 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_TARGET
)
363 rAddonMenuEntry
[i
].Value
>>= rTarget
;
364 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_IMAGEIDENTIFIER
)
365 rAddonMenuEntry
[i
].Value
>>= rImageId
;
366 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_SUBMENU
)
367 rAddonMenuEntry
[i
].Value
>>= rAddonSubMenu
;
368 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_CONTEXT
)
369 rAddonMenuEntry
[i
].Value
>>= rContext
;
373 // Check if the context string matches the provided xModel context
374 bool AddonMenuManager::IsCorrectContext( const OUString
& rModuleIdentifier
, const OUString
& rContext
)
376 if ( rContext
.isEmpty() )
379 if ( !rModuleIdentifier
.isEmpty() )
381 sal_Int32 nIndex
= rContext
.indexOf( rModuleIdentifier
);
382 return ( nIndex
>= 0 );
390 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */