1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_framework.hxx"
26
27 //_________________________________________________________________________________________________________________
28 // my own includes
29 //_________________________________________________________________________________________________________________
30 #include "framework/addonmenu.hxx"
31 #include "framework/addonsoptions.hxx"
32 #include <general.h>
33 #include <macros/debug/assertion.hxx>
34 #include <framework/imageproducer.hxx>
35 #include <framework/menuconfiguration.hxx>
36 #include <services.h>
37
38 //_________________________________________________________________________________________________________________
39 // interface includes
40 //_________________________________________________________________________________________________________________
41 #include <com/sun/star/uno/Reference.hxx>
42 #include <com/sun/star/util/URL.hpp>
43 #include <com/sun/star/util/XURLTransformer.hpp>
44 #include <com/sun/star/frame/XModuleManager.hpp>
45
46 //_________________________________________________________________________________________________________________
47 // includes of other projects
48 //_________________________________________________________________________________________________________________
49 #include <tools/config.hxx>
50 #include <vcl/svapp.hxx>
51 #include <svtools/menuoptions.hxx>
52 #include <svl/solar.hrc>
53 //_________________________________________________________________________________________________________________
54 // namespace
55 //_________________________________________________________________________________________________________________
56
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::frame;
60 using namespace ::com::sun::star::beans;
61
62 // Please look at sfx2/inc/sfxsids.hrc the values are defined there. Due to build dependencies
63 // we cannot include the header file.
64 const sal_uInt16 SID_HELPMENU = (SID_SFX_START + 410);
65 const sal_uInt16 SID_HELP_SUPPORTPAGE = (SID_SFX_START + 1683);
66
67 namespace framework
68 {
69
AddonMenu(const::com::sun::star::uno::Reference<::com::sun::star::frame::XFrame> & rFrame)70 AddonMenu::AddonMenu( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rFrame ) :
71 m_xFrame( rFrame )
72 {
73 }
74
~AddonMenu()75 AddonMenu::~AddonMenu()
76 {
77 for ( sal_uInt16 i = 0; i < GetItemCount(); i++ )
78 {
79 if ( GetItemType( i ) != MENUITEM_SEPARATOR )
80 {
81 // delete user attributes created with new!
82 sal_uInt16 nId = GetItemId( i );
83 MenuConfiguration::Attributes* pUserAttributes = (MenuConfiguration::Attributes*)GetUserValue( nId );
84 delete pUserAttributes;
85 delete GetPopupMenu( nId );
86 }
87 }
88 }
89
90 // ------------------------------------------------------------------------
91
92 // ------------------------------------------------------------------------
93 // Check if command URL string has the unique prefix to identify addon popup menus
IsCommandURLPrefix(const::rtl::OUString & aCmdURL)94 sal_Bool AddonPopupMenu::IsCommandURLPrefix( const ::rtl::OUString& aCmdURL )
95 {
96 const char aPrefixCharBuf[] = ADDONSPOPUPMENU_URL_PREFIX_STR;
97
98 return aCmdURL.matchAsciiL( aPrefixCharBuf, sizeof( aPrefixCharBuf )-1, 0 );
99 }
100
AddonPopupMenu(const com::sun::star::uno::Reference<com::sun::star::frame::XFrame> & rFrame)101 AddonPopupMenu::AddonPopupMenu( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& rFrame ) :
102 AddonMenu( rFrame )
103 {
104 }
105
~AddonPopupMenu()106 AddonPopupMenu::~AddonPopupMenu()
107 {
108 }
109
110 // ------------------------------------------------------------------------
111
GetModuleIdentifier(const Reference<XMultiServiceFactory> & rServiceManager,const Reference<XFrame> & rFrame)112 static ::rtl::OUString GetModuleIdentifier( const Reference< XMultiServiceFactory >& rServiceManager,
113 const Reference< XFrame >& rFrame )
114 {
115 Reference< XModuleManager > xModuleManager( rServiceManager->createInstance( SERVICENAME_MODULEMANAGER ), UNO_QUERY );
116 if ( xModuleManager.is() )
117 {
118 try
119 {
120 return xModuleManager->identify( rFrame );
121 }
122 catch ( Exception& )
123 {
124 }
125 }
126 return ::rtl::OUString();
127 }
128
129 // ------------------------------------------------------------------------
130
HasAddonMenuElements()131 sal_Bool AddonMenuManager::HasAddonMenuElements()
132 {
133 return AddonsOptions().HasAddonsMenu();
134 }
135
HasAddonHelpMenuElements()136 sal_Bool AddonMenuManager::HasAddonHelpMenuElements()
137 {
138 return AddonsOptions().HasAddonsHelpMenu();
139 }
140
141 // Factory method to create different Add-On menu types
CreatePopupMenuType(MenuType eMenuType,const Reference<XFrame> & rFrame)142 PopupMenu* AddonMenuManager::CreatePopupMenuType( MenuType eMenuType, const Reference< XFrame >& rFrame )
143 {
144 if ( eMenuType == ADDON_MENU )
145 return new AddonMenu( rFrame );
146 else if ( eMenuType == ADDON_POPUPMENU )
147 return new AddonPopupMenu( rFrame );
148 else
149 return NULL;
150 }
151
152 // Create the Add-Ons menu
CreateAddonMenu(const Reference<XFrame> & rFrame,const Reference<XMultiServiceFactory> & rServiceManager)153 AddonMenu* AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame,
154 const Reference< XMultiServiceFactory >& rServiceManager )
155 {
156 AddonsOptions aOptions;
157 AddonMenu* pAddonMenu = NULL;
158 sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
159
160 const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
161 if ( rAddonMenuEntries.getLength() > 0 )
162 {
163 pAddonMenu = (AddonMenu *)AddonMenuManager::CreatePopupMenuType( ADDON_MENU, rFrame );
164 ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
165 AddonMenuManager::BuildMenu( pAddonMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
166
167 // Don't return an empty Add-On menu
168 if ( pAddonMenu->GetItemCount() == 0 )
169 {
170 delete pAddonMenu;
171 pAddonMenu = NULL;
172 }
173 }
174
175 return pAddonMenu;
176 }
177
178 // Returns the next insert position from nPos.
GetNextPos(sal_uInt16 nPos)179 sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
180 {
181 return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
182 }
183
184
FindMenuId(Menu * pMenu,const String aCommand)185 static sal_uInt16 FindMenuId( Menu* pMenu, const String aCommand )
186 {
187 sal_uInt16 nPos = 0;
188 String aCmd;
189 for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
190 {
191 sal_uInt16 nId = pMenu->GetItemId( nPos );
192 aCmd = pMenu->GetItemCommand( nId );
193 if ( aCmd == aCommand )
194 return nId;
195 }
196
197 return USHRT_MAX;
198 }
199
200
201 // Merge the Add-Ons help menu items into the given menu bar at a defined pos
MergeAddonHelpMenu(const Reference<XFrame> & rFrame,MenuBar * pMergeMenuBar,const Reference<XMultiServiceFactory> & rServiceManager)202 void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
203 MenuBar* pMergeMenuBar,
204 const Reference< XMultiServiceFactory >& rServiceManager )
205 {
206 if ( pMergeMenuBar )
207 {
208 PopupMenu* pHelpMenu = pMergeMenuBar->GetPopupMenu( SID_HELPMENU );
209 if ( !pHelpMenu )
210 {
211 sal_uInt16 nId = FindMenuId( pMergeMenuBar, String::CreateFromAscii( ".uno:HelpMenu" ));
212 if ( nId != USHRT_MAX )
213 pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
214 }
215
216 if ( pHelpMenu )
217 {
218 static const char REFERENCECOMMAND_AFTER[] = ".uno:HelpSupport";
219 static const char REFERENCECOMMAND_BEFORE[] = ".uno:About";
220
221 // Add-Ons help menu items should be inserted after the "registration" menu item
222 bool bAddAfter = true;
223 sal_uInt16 nItemCount = pHelpMenu->GetItemCount();
224 sal_uInt16 nSupPos = pHelpMenu->GetItemPos( SID_HELP_SUPPORTPAGE );
225 sal_uInt16 nInsPos = nSupPos;
226 sal_uInt16 nInsSepAfterPos = MENU_APPEND;
227 sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
228 AddonsOptions aOptions;
229
230 if ( nSupPos == USHRT_MAX )
231 {
232 // try to detect the online registration dialog menu item with the command URL
233 sal_uInt16 nId = FindMenuId( pHelpMenu, String::CreateFromAscii( REFERENCECOMMAND_AFTER ));
234 nSupPos = pHelpMenu->GetItemPos( nId );
235 nInsPos = nSupPos;
236 }
237
238 if ( nSupPos == USHRT_MAX )
239 {
240 // second try:
241 // try to detect the about menu item with the command URL
242 sal_uInt16 nId = FindMenuId( pHelpMenu, String::CreateFromAscii( REFERENCECOMMAND_BEFORE ));
243 nSupPos = pHelpMenu->GetItemPos( nId );
244 nInsPos = nSupPos;
245 bAddAfter = false;
246 }
247
248 Sequence< Sequence< PropertyValue > > aAddonSubMenu;
249 const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
250
251 nInsPos = bAddAfter ? AddonMenuManager::GetNextPos( nInsPos ) : nInsPos;
252 if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MENUITEM_SEPARATOR )
253 nInsSepAfterPos = nInsPos;
254
255 ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
256 AddonMenuManager::BuildMenu( pHelpMenu, ADDON_MENU, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
257
258 if ( pHelpMenu->GetItemCount() > nItemCount )
259 {
260 if ( nInsSepAfterPos < MENU_APPEND )
261 {
262 nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
263 if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MENUITEM_SEPARATOR )
264 pHelpMenu->InsertSeparator( nInsSepAfterPos );
265 }
266 if ( nSupPos < MENU_APPEND )
267 pHelpMenu->InsertSeparator( nSupPos+1 );
268 else
269 pHelpMenu->InsertSeparator( nItemCount );
270 }
271 }
272 }
273 }
274
275 // Merge the addon popup menus into the given menu bar at the provided pos.
MergeAddonPopupMenus(const Reference<XFrame> & rFrame,sal_uInt16 nMergeAtPos,MenuBar * pMergeMenuBar,const Reference<XMultiServiceFactory> & rServiceManager)276 void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
277 sal_uInt16 nMergeAtPos,
278 MenuBar* pMergeMenuBar,
279 const Reference< XMultiServiceFactory >& rServiceManager )
280 {
281 if ( pMergeMenuBar )
282 {
283 AddonsOptions aAddonsOptions;
284 sal_uInt16 nInsertPos = nMergeAtPos;
285
286 ::rtl::OUString aTitle;
287 ::rtl::OUString aURL;
288 ::rtl::OUString aTarget;
289 ::rtl::OUString aImageId;
290 ::rtl::OUString aContext;
291 Sequence< Sequence< PropertyValue > > aAddonSubMenu;
292 sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
293 ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
294
295 const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
296 for ( sal_Int32 i = 0; i < rAddonMenuEntries.getLength(); i++ )
297 {
298 AddonMenuManager::GetMenuEntry( rAddonMenuEntries[i],
299 aTitle,
300 aURL,
301 aTarget,
302 aImageId,
303 aContext,
304 aAddonSubMenu );
305 if ( aTitle.getLength() > 0 &&
306 aURL.getLength() > 0 &&
307 aAddonSubMenu.getLength() > 0 &&
308 AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
309 {
310 sal_uInt16 nId = nUniqueMenuId++;
311 AddonPopupMenu* pAddonPopupMenu = (AddonPopupMenu *)AddonMenuManager::CreatePopupMenuType( ADDON_POPUPMENU, rFrame );
312
313 AddonMenuManager::BuildMenu( pAddonPopupMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
314
315 if ( pAddonPopupMenu->GetItemCount() > 0 )
316 {
317 pAddonPopupMenu->SetCommandURL( aURL );
318 pMergeMenuBar->InsertItem( nId, aTitle, 0, nInsertPos++ );
319 pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
320
321 // Store the command URL into the VCL menu bar for later identification
322 pMergeMenuBar->SetItemCommand( nId, aURL );
323 }
324 else
325 delete pAddonPopupMenu;
326 }
327 }
328 }
329 }
330
331 // Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
BuildMenu(PopupMenu * pCurrentMenu,MenuType nSubMenuType,sal_uInt16 nInsPos,sal_uInt16 & nUniqueMenuId,Sequence<Sequence<PropertyValue>> aAddonMenuDefinition,const Reference<XFrame> & rFrame,const::rtl::OUString & rModuleIdentifier)332 void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu,
333 MenuType nSubMenuType,
334 sal_uInt16 nInsPos,
335 sal_uInt16& nUniqueMenuId,
336 Sequence< Sequence< PropertyValue > > aAddonMenuDefinition,
337 const Reference< XFrame >& rFrame,
338 const ::rtl::OUString& rModuleIdentifier )
339 {
340 Sequence< Sequence< PropertyValue > > aAddonSubMenu;
341 sal_Bool bInsertSeparator = sal_False;
342 sal_uInt32 i = 0;
343 sal_uInt32 nElements = 0;
344 sal_uInt32 nCount = aAddonMenuDefinition.getLength();
345 AddonsOptions aAddonsOptions;
346
347 ::rtl::OUString aTitle;
348 ::rtl::OUString aURL;
349 ::rtl::OUString aTarget;
350 ::rtl::OUString aImageId;
351 ::rtl::OUString aContext;
352
353 for ( i = 0; i < nCount; ++i )
354 {
355 GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aImageId, aContext, aAddonSubMenu );
356
357 if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( !aTitle.getLength() && !aURL.getLength() ))
358 continue;
359
360 if ( aURL == ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:separator" )))
361 bInsertSeparator = sal_True;
362 else
363 {
364 PopupMenu* pSubMenu = NULL;
365 if ( aAddonSubMenu.getLength() > 0 )
366 {
367 pSubMenu = AddonMenuManager::CreatePopupMenuType( nSubMenuType, rFrame );
368 AddonMenuManager::BuildMenu( pSubMenu, nSubMenuType, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
369
370 // Don't create a menu item for an empty sub menu
371 if ( pSubMenu->GetItemCount() == 0 )
372 {
373 delete pSubMenu;
374 pSubMenu = NULL;
375 continue;
376 }
377 }
378
379 if ( bInsertSeparator && nElements > 0 )
380 {
381 // Insert a separator only when we insert a new element afterwards and we
382 // have already one before us
383 nElements = 0;
384 bInsertSeparator = sal_False;
385 pCurrentMenu->InsertSeparator( nInsPos );
386 nInsPos = AddonMenuManager::GetNextPos( nInsPos );
387 }
388
389 sal_uInt16 nId = nUniqueMenuId++;
390 pCurrentMenu->InsertItem( nId, aTitle, 0, nInsPos );
391 nInsPos = AddonMenuManager::GetNextPos( nInsPos );
392
393 ++nElements;
394
395 // Store values from configuration to the New and Wizard menu entries to enable
396 // sfx2 based code to support high contrast mode correctly!
397 pCurrentMenu->SetUserValue( nId, sal_uIntPtr( new MenuConfiguration::Attributes( aTarget, aImageId )) );
398 pCurrentMenu->SetItemCommand( nId, aURL );
399
400 if ( pSubMenu )
401 pCurrentMenu->SetPopupMenu( nId, pSubMenu );
402 }
403 }
404 }
405
406 // Retrieve the menu entry property values from a sequence
GetMenuEntry(const Sequence<PropertyValue> & rAddonMenuEntry,::rtl::OUString & rTitle,::rtl::OUString & rURL,::rtl::OUString & rTarget,::rtl::OUString & rImageId,::rtl::OUString & rContext,Sequence<Sequence<PropertyValue>> & rAddonSubMenu)407 void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
408 ::rtl::OUString& rTitle,
409 ::rtl::OUString& rURL,
410 ::rtl::OUString& rTarget,
411 ::rtl::OUString& rImageId,
412 ::rtl::OUString& rContext,
413 Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
414 {
415 // Reset submenu parameter
416 rAddonSubMenu = Sequence< Sequence< PropertyValue > >();
417
418 for ( int i = 0; i < rAddonMenuEntry.getLength(); i++ )
419 {
420 ::rtl::OUString aMenuEntryPropName = rAddonMenuEntry[i].Name;
421 if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_URL )
422 rAddonMenuEntry[i].Value >>= rURL;
423 else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TITLE )
424 rAddonMenuEntry[i].Value >>= rTitle;
425 else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TARGET )
426 rAddonMenuEntry[i].Value >>= rTarget;
427 else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_IMAGEIDENTIFIER )
428 rAddonMenuEntry[i].Value >>= rImageId;
429 else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_SUBMENU )
430 rAddonMenuEntry[i].Value >>= rAddonSubMenu;
431 else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_CONTEXT )
432 rAddonMenuEntry[i].Value >>= rContext;
433 }
434 }
435
436 // Check if the context string matches the provided xModel context
IsCorrectContext(const::rtl::OUString & rModuleIdentifier,const::rtl::OUString & aContext)437 sal_Bool AddonMenuManager::IsCorrectContext( const ::rtl::OUString& rModuleIdentifier, const ::rtl::OUString& aContext )
438 {
439 if ( aContext.isEmpty() )
440 return sal_True;
441
442 if ( !rModuleIdentifier.isEmpty() )
443 {
444 sal_Int32 nIndex = aContext.indexOf( rModuleIdentifier );
445 return ( nIndex >= 0 );
446 }
447
448 return sal_False;
449 }
450
451 }
452
453