Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <framework/addonmenu.hxx>
21 : #include <framework/addonsoptions.hxx>
22 : #include <general.h>
23 : #include <framework/imageproducer.hxx>
24 : #include <framework/menuconfiguration.hxx>
25 : #include <services.h>
26 :
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>
32 :
33 : #include <tools/config.hxx>
34 : #include <vcl/svapp.hxx>
35 : #include <svtools/menuoptions.hxx>
36 : #include <svl/solar.hrc>
37 :
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;
42 :
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);
46 :
47 : namespace framework
48 : {
49 :
50 1494 : AddonMenu::AddonMenu( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rFrame ) :
51 1494 : m_xFrame( rFrame )
52 : {
53 1494 : }
54 :
55 2988 : AddonMenu::~AddonMenu()
56 : {
57 23904 : for ( sal_uInt16 i = 0; i < GetItemCount(); i++ )
58 : {
59 22410 : if ( GetItemType( i ) != MenuItemType::SEPARATOR )
60 : {
61 : // delete user attributes created with new!
62 17928 : sal_uInt16 nId = GetItemId( i );
63 17928 : MenuConfiguration::Attributes* pUserAttributes = reinterpret_cast<MenuConfiguration::Attributes*>(GetUserValue( nId ));
64 17928 : delete pUserAttributes;
65 17928 : delete GetPopupMenu( nId );
66 : }
67 : }
68 1494 : }
69 :
70 : // Check if command URL string has the unique prefix to identify addon popup menus
71 0 : bool AddonPopupMenu::IsCommandURLPrefix( const OUString& aCmdURL )
72 : {
73 0 : const char aPrefixCharBuf[] = ADDONSPOPUPMENU_URL_PREFIX_STR;
74 :
75 0 : return aCmdURL.matchAsciiL( aPrefixCharBuf, sizeof( aPrefixCharBuf )-1, 0 );
76 : }
77 :
78 0 : AddonPopupMenu::AddonPopupMenu( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& rFrame ) :
79 0 : AddonMenu( rFrame )
80 : {
81 0 : }
82 :
83 0 : AddonPopupMenu::~AddonPopupMenu()
84 : {
85 0 : }
86 :
87 2962 : static OUString GetModuleIdentifier(const Reference<XComponentContext>& rContext,
88 : const Reference< XFrame >& rFrame)
89 : {
90 2962 : Reference< XModuleManager > xModuleManager(ModuleManager::create(rContext));
91 2962 : if ( xModuleManager.is() )
92 : {
93 : try
94 : {
95 2962 : return xModuleManager->identify( rFrame );
96 : }
97 0 : catch ( Exception& )
98 : {
99 : }
100 : }
101 0 : return OUString();
102 : }
103 :
104 1482 : bool AddonMenuManager::HasAddonMenuElements()
105 : {
106 1482 : return AddonsOptions().HasAddonsMenu();
107 : }
108 :
109 : // Factory method to create different Add-On menu types
110 0 : PopupMenu* AddonMenuManager::CreatePopupMenuType( MenuType eMenuType, const Reference< XFrame >& rFrame )
111 : {
112 0 : if ( eMenuType == ADDON_MENU )
113 0 : return new AddonMenu( rFrame );
114 0 : else if ( eMenuType == ADDON_POPUPMENU )
115 0 : return new AddonPopupMenu( rFrame );
116 : else
117 0 : return NULL;
118 : }
119 :
120 : // Create the Add-Ons menu
121 0 : AddonMenu* AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame,
122 : const Reference< XComponentContext >& rContext )
123 : {
124 0 : AddonsOptions aOptions;
125 0 : AddonMenu* pAddonMenu = NULL;
126 0 : sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
127 :
128 0 : const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
129 0 : if ( rAddonMenuEntries.getLength() > 0 )
130 : {
131 0 : pAddonMenu = static_cast<AddonMenu *>(AddonMenuManager::CreatePopupMenuType( ADDON_MENU, rFrame ));
132 0 : ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rContext, rFrame );
133 0 : AddonMenuManager::BuildMenu( pAddonMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
134 :
135 : // Don't return an empty Add-On menu
136 0 : if ( pAddonMenu->GetItemCount() == 0 )
137 : {
138 0 : delete pAddonMenu;
139 0 : pAddonMenu = NULL;
140 0 : }
141 : }
142 :
143 0 : return pAddonMenu;
144 : }
145 :
146 : // Returns the next insert position from nPos.
147 0 : sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
148 : {
149 0 : return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
150 : }
151 :
152 6972 : static sal_uInt16 FindMenuId( Menu* pMenu, const OUString& aCommand )
153 : {
154 6972 : sal_uInt16 nPos = 0;
155 6972 : OUString aCmd;
156 27632 : for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
157 : {
158 23624 : sal_uInt16 nId = pMenu->GetItemId( nPos );
159 23624 : aCmd = pMenu->GetItemCommand( nId );
160 23624 : if ( aCmd == aCommand )
161 2964 : return nId;
162 : }
163 :
164 4008 : return USHRT_MAX;
165 : }
166 :
167 : // Merge the Add-Ons help menu items into the given menu bar at a defined pos
168 5490 : void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
169 : MenuBar* pMergeMenuBar,
170 : const Reference<XComponentContext>& rContext )
171 : {
172 5490 : if ( pMergeMenuBar )
173 : {
174 5490 : PopupMenu* pHelpMenu = pMergeMenuBar->GetPopupMenu( SID_HELPMENU );
175 5490 : if ( !pHelpMenu )
176 : {
177 5490 : sal_uInt16 nId = FindMenuId(pMergeMenuBar, OUString(".uno:HelpMenu"));
178 5490 : if ( nId != USHRT_MAX )
179 1482 : pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
180 : }
181 :
182 5490 : if ( pHelpMenu )
183 : {
184 : // Add-Ons help menu items should be inserted after the "registration" menu item
185 1482 : sal_uInt16 nItemCount = pHelpMenu->GetItemCount();
186 1482 : sal_uInt16 nInsSepAfterPos = MENU_APPEND;
187 1482 : sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
188 1482 : AddonsOptions aOptions;
189 :
190 : // try to detect the about menu item with the command URL
191 1482 : sal_uInt16 nId = FindMenuId(pHelpMenu, OUString(".uno:About"));
192 1482 : sal_uInt16 nInsPos = pHelpMenu->GetItemPos( nId );
193 :
194 1482 : const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
195 :
196 1482 : if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MenuItemType::SEPARATOR )
197 1482 : nInsSepAfterPos = nInsPos;
198 :
199 2964 : ::rtl::OUString aModuleIdentifier = GetModuleIdentifier(rContext, rFrame);
200 1482 : AddonMenuManager::BuildMenu( pHelpMenu, ADDON_MENU, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
201 :
202 1482 : if ( pHelpMenu->GetItemCount() > nItemCount )
203 : {
204 0 : if ( nInsSepAfterPos < MENU_APPEND )
205 : {
206 0 : nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
207 0 : if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MenuItemType::SEPARATOR )
208 0 : pHelpMenu->InsertSeparator(OString(), nInsSepAfterPos);
209 : }
210 0 : pHelpMenu->InsertSeparator(OString(), nItemCount);
211 1482 : }
212 : }
213 : }
214 5490 : }
215 :
216 : // Merge the addon popup menus into the given menu bar at the provided pos.
217 1480 : void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
218 : sal_uInt16 nMergeAtPos,
219 : MenuBar* pMergeMenuBar,
220 : const Reference< XComponentContext >& rContext )
221 : {
222 1480 : if ( pMergeMenuBar )
223 : {
224 1480 : AddonsOptions aAddonsOptions;
225 1480 : sal_uInt16 nInsertPos = nMergeAtPos;
226 :
227 2960 : OUString aTitle;
228 2960 : OUString aURL;
229 2960 : OUString aTarget;
230 2960 : OUString aImageId;
231 2960 : OUString aContext;
232 2960 : Sequence< Sequence< PropertyValue > > aAddonSubMenu;
233 1480 : sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
234 2960 : OUString aModuleIdentifier = GetModuleIdentifier(rContext, rFrame);
235 :
236 1480 : const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
237 1480 : for ( sal_Int32 i = 0; i < rAddonMenuEntries.getLength(); i++ )
238 : {
239 0 : AddonMenuManager::GetMenuEntry( rAddonMenuEntries[i],
240 : aTitle,
241 : aURL,
242 : aTarget,
243 : aImageId,
244 : aContext,
245 0 : aAddonSubMenu );
246 0 : if ( !aTitle.isEmpty() &&
247 0 : !aURL.isEmpty() &&
248 0 : aAddonSubMenu.getLength() > 0 &&
249 0 : AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
250 : {
251 0 : sal_uInt16 nId = nUniqueMenuId++;
252 0 : AddonPopupMenu* pAddonPopupMenu = static_cast<AddonPopupMenu *>(AddonMenuManager::CreatePopupMenuType( ADDON_POPUPMENU, rFrame ));
253 :
254 0 : AddonMenuManager::BuildMenu( pAddonPopupMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
255 :
256 0 : if ( pAddonPopupMenu->GetItemCount() > 0 )
257 : {
258 0 : pAddonPopupMenu->SetCommandURL( aURL );
259 0 : pMergeMenuBar->InsertItem( nId, aTitle, MenuItemBits::NONE, OString(), nInsertPos++ );
260 0 : pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
261 :
262 : // Store the command URL into the VCL menu bar for later identification
263 0 : pMergeMenuBar->SetItemCommand( nId, aURL );
264 : }
265 : else
266 0 : delete pAddonPopupMenu;
267 : }
268 1480 : }
269 : }
270 1480 : }
271 :
272 : // Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
273 1482 : void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu,
274 : MenuType nSubMenuType,
275 : sal_uInt16 nInsPos,
276 : sal_uInt16& nUniqueMenuId,
277 : const Sequence< Sequence< PropertyValue > >& aAddonMenuDefinition,
278 : const Reference< XFrame >& rFrame,
279 : const ::rtl::OUString& rModuleIdentifier )
280 : {
281 1482 : Sequence< Sequence< PropertyValue > > aAddonSubMenu;
282 1482 : bool bInsertSeparator = false;
283 1482 : sal_uInt32 i = 0;
284 1482 : sal_uInt32 nElements = 0;
285 1482 : sal_uInt32 nCount = aAddonMenuDefinition.getLength();
286 2964 : AddonsOptions aAddonsOptions;
287 :
288 2964 : OUString aTitle;
289 2964 : OUString aURL;
290 2964 : OUString aTarget;
291 2964 : OUString aImageId;
292 2964 : OUString aContext;
293 :
294 1482 : for ( i = 0; i < nCount; ++i )
295 : {
296 0 : GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aImageId, aContext, aAddonSubMenu );
297 :
298 0 : if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( aTitle.isEmpty() && aURL.isEmpty() ))
299 0 : continue;
300 :
301 0 : if ( aURL == "private:separator" )
302 0 : bInsertSeparator = true;
303 : else
304 : {
305 0 : PopupMenu* pSubMenu = NULL;
306 0 : if ( aAddonSubMenu.getLength() > 0 )
307 : {
308 0 : pSubMenu = AddonMenuManager::CreatePopupMenuType( nSubMenuType, rFrame );
309 0 : AddonMenuManager::BuildMenu( pSubMenu, nSubMenuType, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
310 :
311 : // Don't create a menu item for an empty sub menu
312 0 : if ( pSubMenu->GetItemCount() == 0 )
313 : {
314 0 : delete pSubMenu;
315 0 : pSubMenu = NULL;
316 0 : continue;
317 : }
318 : }
319 :
320 0 : if ( bInsertSeparator && nElements > 0 )
321 : {
322 : // Insert a separator only when we insert a new element afterwards and we
323 : // have already one before us
324 0 : nElements = 0;
325 0 : bInsertSeparator = false;
326 0 : pCurrentMenu->InsertSeparator(OString(), nInsPos);
327 0 : nInsPos = AddonMenuManager::GetNextPos( nInsPos );
328 : }
329 :
330 0 : sal_uInt16 nId = nUniqueMenuId++;
331 0 : pCurrentMenu->InsertItem(nId, aTitle, MenuItemBits::NONE, OString(), nInsPos);
332 0 : nInsPos = AddonMenuManager::GetNextPos( nInsPos );
333 :
334 0 : ++nElements;
335 :
336 : // Store values from configuration to the New and Wizard menu entries to enable
337 : // sfx2 based code to support high contrast mode correctly!
338 0 : pCurrentMenu->SetUserValue( nId, sal_uIntPtr( new MenuConfiguration::Attributes( aTarget, aImageId )) );
339 0 : pCurrentMenu->SetItemCommand( nId, aURL );
340 :
341 0 : if ( pSubMenu )
342 0 : pCurrentMenu->SetPopupMenu( nId, pSubMenu );
343 : }
344 1482 : }
345 1482 : }
346 :
347 : // Retrieve the menu entry property values from a sequence
348 0 : void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
349 : OUString& rTitle,
350 : OUString& rURL,
351 : OUString& rTarget,
352 : OUString& rImageId,
353 : OUString& rContext,
354 : Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
355 : {
356 : // Reset submenu parameter
357 0 : rAddonSubMenu = Sequence< Sequence< PropertyValue > >();
358 :
359 0 : for ( int i = 0; i < rAddonMenuEntry.getLength(); i++ )
360 : {
361 0 : OUString aMenuEntryPropName = rAddonMenuEntry[i].Name;
362 0 : if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_URL )
363 0 : rAddonMenuEntry[i].Value >>= rURL;
364 0 : else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TITLE )
365 0 : rAddonMenuEntry[i].Value >>= rTitle;
366 0 : else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TARGET )
367 0 : rAddonMenuEntry[i].Value >>= rTarget;
368 0 : else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_IMAGEIDENTIFIER )
369 0 : rAddonMenuEntry[i].Value >>= rImageId;
370 0 : else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_SUBMENU )
371 0 : rAddonMenuEntry[i].Value >>= rAddonSubMenu;
372 0 : else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_CONTEXT )
373 0 : rAddonMenuEntry[i].Value >>= rContext;
374 0 : }
375 0 : }
376 :
377 : // Check if the context string matches the provided xModel context
378 0 : bool AddonMenuManager::IsCorrectContext( const OUString& rModuleIdentifier, const OUString& rContext )
379 : {
380 0 : if ( rContext.isEmpty() )
381 0 : return true;
382 :
383 0 : if ( !rModuleIdentifier.isEmpty() )
384 : {
385 0 : sal_Int32 nIndex = rContext.indexOf( rModuleIdentifier );
386 0 : return ( nIndex >= 0 );
387 : }
388 :
389 0 : return false;
390 : }
391 :
392 969 : }
393 :
394 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|