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 70 AddonMenu::AddonMenu( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rFrame ) : 71 m_xFrame( rFrame ) 72 { 73 } 74 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 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 101 AddonPopupMenu::AddonPopupMenu( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& rFrame ) : 102 AddonMenu( rFrame ) 103 { 104 } 105 106 AddonPopupMenu::~AddonPopupMenu() 107 { 108 } 109 110 // ------------------------------------------------------------------------ 111 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 131 sal_Bool AddonMenuManager::HasAddonMenuElements() 132 { 133 return AddonsOptions().HasAddonsMenu(); 134 } 135 136 sal_Bool AddonMenuManager::HasAddonHelpMenuElements() 137 { 138 return AddonsOptions().HasAddonsHelpMenu(); 139 } 140 141 // Factory method to create different Add-On menu types 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 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. 179 sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos ) 180 { 181 return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 ); 182 } 183 184 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 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. 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 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 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 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