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