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 #include <uielement/menubarmerger.hxx> 32 #include <framework/addonsoptions.hxx> 33 34 using namespace ::com::sun::star; 35 36 static const char SEPARATOR_STRING[] = "private:separator"; 37 static const sal_uInt32 SEPARATOR_STRING_LEN = 17; 38 39 static const char MERGECOMMAND_ADDAFTER[] = "AddAfter"; 40 static const sal_uInt32 MERGECOMMAND_ADDAFTER_LEN = 8; 41 static const char MERGECOMMAND_ADDBEFORE[] = "AddBefore"; 42 static const sal_uInt32 MERGECOMMAND_ADDBEFORE_LEN = 9; 43 static const char MERGECOMMAND_REPLACE[] = "Replace"; 44 static const sal_uInt32 MERGECOMMAND_REPLACE_LEN = 7; 45 static const char MERGECOMMAND_REMOVE[] = "Remove"; 46 static const sal_uInt32 MERGECOMMAND_REMOVE_LEN = 6; 47 48 static const char MERGEFALLBACK_ADDPATH[] = "AddPath"; 49 static const char MERGEFALLBACK_ADDPATH_LEN = 7; 50 static const char MERGEFALLBACK_IGNORE[] = "Ignore"; 51 static const char MERGEFALLBACK_IGNORE_LEN = 6; 52 53 54 namespace framework 55 { 56 57 /** 58 Check whether a module identifier is part of a context 59 defined by a colon separated list of module identifier. 60 61 @param 62 rContext 63 64 Describes a context string list where all contexts 65 are delimited by a colon. For more information about 66 the module identifier used as context strings see the 67 IDL description of com::sun::star::frame::XModuleManager 68 69 @param 70 rModuleIdentifier 71 72 A string describing a module identifier. See IDL 73 description of com::sun::star::frame::XModuleManager. 74 75 */ 76 bool MenuBarMerger::IsCorrectContext( const ::rtl::OUString& rContext, const ::rtl::OUString& rModuleIdentifier ) 77 { 78 return (( rContext.getLength() == 0 ) || ( rContext.indexOf( rModuleIdentifier ) >= 0 )); 79 } 80 81 void MenuBarMerger::RetrieveReferencePath( 82 const ::rtl::OUString& rReferencePathString, 83 ::std::vector< ::rtl::OUString >& rReferencePath ) 84 { 85 const sal_Char aDelimiter = '\\'; 86 87 rReferencePath.clear(); 88 sal_Int32 nIndex( 0 ); 89 do 90 { 91 ::rtl::OUString aToken = rReferencePathString.getToken( 0, aDelimiter, nIndex ); 92 if ( aToken.getLength() > 0 ) 93 rReferencePath.push_back( aToken ); 94 } 95 while ( nIndex >= 0 ); 96 } 97 98 ReferencePathInfo MenuBarMerger::FindReferencePath( 99 const ::std::vector< ::rtl::OUString >& rReferencePath, 100 Menu* pMenu ) 101 { 102 sal_uInt32 i( 0 ); 103 const sal_uInt32 nCount( rReferencePath.size() ); 104 Menu* pCurrMenu( pMenu ); 105 RPResultInfo eResult( RP_OK ); 106 107 sal_Int32 nLevel( - 1 ); 108 sal_uInt16 nPos( MENU_ITEM_NOTFOUND ); 109 do 110 { 111 ++nLevel; 112 ::rtl::OUString aCmd( rReferencePath[i] ); 113 114 if ( i == nCount-1 ) 115 { 116 // Check last reference path element. Must be a leave (menu item). 117 sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); 118 if ( nTmpPos != MENU_ITEM_NOTFOUND ) 119 nPos = nTmpPos; 120 eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND; 121 } 122 else 123 { 124 // Check reference path element. Must be a node (popup menu)! 125 sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu ); 126 if ( nTmpPos != MENU_ITEM_NOTFOUND ) 127 { 128 sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos ); 129 Menu* pTmpMenu = pCurrMenu->GetPopupMenu( nItemId ); 130 if ( pTmpMenu != 0 ) 131 pCurrMenu = pTmpMenu; 132 else 133 { 134 nPos = nTmpPos; 135 eResult = RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND; 136 } 137 } 138 else 139 eResult = RP_POPUPMENU_NOT_FOUND; 140 } 141 i++; 142 } 143 while (( pCurrMenu != 0 ) && ( i < nCount ) && ( eResult == RP_OK )); 144 145 ReferencePathInfo aResult; 146 aResult.pPopupMenu = pCurrMenu; 147 aResult.nPos = nPos; 148 aResult.nLevel = nLevel; 149 aResult.eResult = eResult; 150 151 return aResult; 152 } 153 154 sal_uInt16 MenuBarMerger::FindMenuItem( const ::rtl::OUString& rCmd, Menu* pCurrMenu ) 155 { 156 for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ ) 157 { 158 const sal_uInt16 nItemId = pCurrMenu->GetItemId( i ); 159 if ( nItemId > 0 ) 160 { 161 if ( rCmd == ::rtl::OUString( pCurrMenu->GetItemCommand( nItemId ))) 162 return i; 163 } 164 } 165 166 return MENU_ITEM_NOTFOUND; 167 } 168 169 bool MenuBarMerger::CreateSubMenu( 170 Menu* pSubMenu, 171 sal_uInt16& nItemId, 172 const ::rtl::OUString& rModuleIdentifier, 173 const AddonMenuContainer& rAddonSubMenu ) 174 { 175 const sal_uInt32 nSize = rAddonSubMenu.size(); 176 for ( sal_uInt32 i = 0; i < nSize; i++ ) 177 { 178 const AddonMenuItem& rMenuItem = rAddonSubMenu[i]; 179 180 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) 181 { 182 if ( rMenuItem.aURL.equalsAsciiL( SEPARATOR_STRING, SEPARATOR_STRING_LEN )) 183 { 184 pSubMenu->InsertSeparator( MENU_APPEND ); 185 } 186 else 187 { 188 pSubMenu->InsertItem( nItemId, rMenuItem.aTitle, 0, MENU_APPEND ); 189 pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL ); 190 if ( !rMenuItem.aSubMenu.empty() ) 191 { 192 PopupMenu* pPopupMenu = new PopupMenu(); 193 pSubMenu->SetPopupMenu( nItemId, pPopupMenu ); 194 ++nItemId; 195 196 CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); 197 } 198 else 199 ++nItemId; 200 } 201 } 202 } 203 204 return true; 205 } 206 207 bool MenuBarMerger::MergeMenuItems( 208 Menu* pMenu, 209 sal_uInt16 nPos, 210 sal_uInt16 nModIndex, 211 sal_uInt16& nItemId, 212 const ::rtl::OUString& rModuleIdentifier, 213 const AddonMenuContainer& rAddonMenuItems ) 214 { 215 sal_uInt16 nIndex( 0 ); 216 const sal_uInt32 nSize = rAddonMenuItems.size(); 217 for ( sal_uInt32 i = 0; i < nSize; i++ ) 218 { 219 const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; 220 221 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) 222 { 223 if ( rMenuItem.aURL.equalsAsciiL( SEPARATOR_STRING, SEPARATOR_STRING_LEN )) 224 { 225 pMenu->InsertSeparator( nPos+nModIndex+nIndex ); 226 } 227 else 228 { 229 pMenu->InsertItem( nItemId, rMenuItem.aTitle, 0, nPos+nModIndex+nIndex ); 230 pMenu->SetItemCommand( nItemId, rMenuItem.aURL ); 231 if ( !rMenuItem.aSubMenu.empty() ) 232 { 233 PopupMenu* pSubMenu = new PopupMenu(); 234 pMenu->SetPopupMenu( nItemId, pSubMenu ); 235 ++nItemId; 236 237 CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu ); 238 } 239 else 240 ++nItemId; 241 } 242 ++nIndex; 243 } 244 } 245 246 return true; 247 } 248 249 bool MenuBarMerger::ReplaceMenuItem( 250 Menu* pMenu, 251 sal_uInt16 nPos, 252 sal_uInt16& rItemId, 253 const ::rtl::OUString& rModuleIdentifier, 254 const AddonMenuContainer& rAddonMenuItems ) 255 { 256 // There is no replace available. Therfore we first have to 257 // remove the old menu entry, 258 pMenu->RemoveItem( nPos ); 259 260 return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems ); 261 } 262 263 bool MenuBarMerger::RemoveMenuItems( 264 Menu* pMenu, 265 sal_uInt16 nPos, 266 const ::rtl::OUString& rMergeCommandParameter ) 267 { 268 const sal_uInt16 nParam( sal_uInt16( rMergeCommandParameter.toInt32() )); 269 sal_uInt16 nCount( 1 ); 270 271 nCount = std::max( nParam, nCount ); 272 273 sal_uInt16 i = 0; 274 while (( nPos < pMenu->GetItemCount() ) && ( i < nCount )) 275 { 276 pMenu->RemoveItem( nPos ); 277 ++i; 278 } 279 280 return true; 281 } 282 283 bool MenuBarMerger::ProcessMergeOperation( 284 Menu* pMenu, 285 sal_uInt16 nPos, 286 sal_uInt16& nItemId, 287 const ::rtl::OUString& rMergeCommand, 288 const ::rtl::OUString& rMergeCommandParameter, 289 const ::rtl::OUString& rModuleIdentifier, 290 const AddonMenuContainer& rAddonMenuItems ) 291 { 292 sal_uInt16 nModIndex( 0 ); 293 294 if ( rMergeCommand.equalsAsciiL( MERGECOMMAND_ADDBEFORE, MERGECOMMAND_ADDBEFORE_LEN )) 295 { 296 nModIndex = 0; 297 return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); 298 } 299 else if ( rMergeCommand.equalsAsciiL( MERGECOMMAND_ADDAFTER, MERGECOMMAND_ADDAFTER_LEN )) 300 { 301 nModIndex = 1; 302 return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems ); 303 } 304 else if ( rMergeCommand.equalsAsciiL( MERGECOMMAND_REPLACE, MERGECOMMAND_REPLACE_LEN )) 305 { 306 return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems ); 307 } 308 else if ( rMergeCommand.equalsAsciiL( MERGECOMMAND_REMOVE, MERGECOMMAND_REMOVE_LEN )) 309 { 310 return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter ); 311 } 312 313 return false; 314 } 315 316 bool MenuBarMerger::ProcessFallbackOperation( 317 const ReferencePathInfo& aRefPathInfo, 318 sal_uInt16& rItemId, 319 const ::rtl::OUString& rMergeCommand, 320 const ::rtl::OUString& rMergeFallback, 321 const ::std::vector< ::rtl::OUString >& rReferencePath, 322 const ::rtl::OUString& rModuleIdentifier, 323 const AddonMenuContainer& rAddonMenuItems ) 324 { 325 if (( rMergeFallback.equalsAsciiL( MERGEFALLBACK_IGNORE, MERGEFALLBACK_IGNORE_LEN )) || 326 ( rMergeCommand.equalsAsciiL( MERGECOMMAND_REPLACE, MERGECOMMAND_REPLACE_LEN )) || 327 ( rMergeCommand.equalsAsciiL( MERGECOMMAND_REMOVE, MERGECOMMAND_REMOVE_LEN )) ) 328 { 329 return true; 330 } 331 else if ( rMergeFallback.equalsAsciiL( MERGEFALLBACK_ADDPATH, MERGEFALLBACK_ADDPATH_LEN )) 332 { 333 Menu* pCurrMenu( aRefPathInfo.pPopupMenu ); 334 sal_Int32 nLevel( aRefPathInfo.nLevel ); 335 const sal_Int32 nSize( rReferencePath.size() ); 336 bool bFirstLevel( true ); 337 338 while ( nLevel < nSize ) 339 { 340 if ( nLevel == nSize-1 ) 341 { 342 const sal_uInt32 nCount = rAddonMenuItems.size(); 343 for ( sal_uInt32 i = 0; i < nCount; ++i ) 344 { 345 const AddonMenuItem& rMenuItem = rAddonMenuItems[i]; 346 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier )) 347 { 348 if ( rMenuItem.aURL.equalsAsciiL( SEPARATOR_STRING, SEPARATOR_STRING_LEN )) 349 pCurrMenu->InsertSeparator( MENU_APPEND ); 350 else 351 { 352 pCurrMenu->InsertItem( rItemId, rMenuItem.aTitle, 0, MENU_APPEND ); 353 pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL ); 354 ++rItemId; 355 } 356 } 357 } 358 } 359 else 360 { 361 const ::rtl::OUString aCmd( rReferencePath[nLevel] ); 362 363 sal_uInt16 nInsPos( MENU_APPEND ); 364 PopupMenu* pPopupMenu( new PopupMenu ); 365 366 if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND )) 367 { 368 // special case: menu item without popup 369 nInsPos = aRefPathInfo.nPos; 370 sal_uInt16 nSetItemId = pCurrMenu->GetItemId( nInsPos ); 371 pCurrMenu->SetItemCommand( nSetItemId, aCmd ); 372 pCurrMenu->SetPopupMenu( nSetItemId, pPopupMenu ); 373 } 374 else 375 { 376 // normal case: insert a new item with popup 377 pCurrMenu->InsertItem( rItemId, ::rtl::OUString(), 0, MENU_APPEND ); 378 pCurrMenu->SetItemCommand( rItemId, aCmd ); 379 pCurrMenu->SetPopupMenu( rItemId, pPopupMenu ); 380 } 381 382 pCurrMenu = pPopupMenu; 383 ++rItemId; 384 bFirstLevel = false; 385 } 386 ++nLevel; 387 } 388 return true; 389 } 390 391 return false; 392 } 393 394 void MenuBarMerger::GetMenuEntry( 395 const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry, 396 AddonMenuItem& rAddonMenuItem ) 397 { 398 // Reset submenu member 399 rAddonMenuItem.aSubMenu.clear(); 400 401 for ( sal_Int32 i = 0; i < rAddonMenuEntry.getLength(); i++ ) 402 { 403 ::rtl::OUString aMenuEntryPropName = rAddonMenuEntry[i].Name; 404 if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_URL, ADDONSMENUITEM_URL_LEN )) 405 rAddonMenuEntry[i].Value >>= rAddonMenuItem.aURL; 406 else if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_TITLE, ADDONSMENUITEM_TITLE_LEN )) 407 rAddonMenuEntry[i].Value >>= rAddonMenuItem.aTitle; 408 else if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_TARGET, ADDONSMENUITEM_TARGET_LEN )) 409 rAddonMenuEntry[i].Value >>= rAddonMenuItem.aTarget; 410 else if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_SUBMENU, ADDONSMENUITEM_SUBMENU_LEN )) 411 { 412 uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu; 413 rAddonMenuEntry[i].Value >>= aSubMenu; 414 GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu ); 415 } 416 else if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_CONTEXT, ADDONSMENUITEM_CONTEXT_LEN )) 417 rAddonMenuEntry[i].Value >>= rAddonMenuItem.aContext; 418 else if ( aMenuEntryPropName.equalsAsciiL( ADDONSMENUITEM_STRING_IMAGEIDENTIFIER, ADDONSMENUITEM_IMAGEIDENTIFIER_LEN )) 419 rAddonMenuEntry[i].Value >>= rAddonMenuItem.aImageId; 420 } 421 } 422 423 void MenuBarMerger::GetSubMenu( 424 const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries, 425 AddonMenuContainer& rSubMenu ) 426 { 427 rSubMenu.clear(); 428 429 const sal_Int32 nCount = rSubMenuEntries.getLength(); 430 rSubMenu.reserve(rSubMenu.size() + nCount); 431 for ( sal_Int32 i = 0; i < nCount; i++ ) 432 { 433 const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ]; 434 435 AddonMenuItem aMenuItem; 436 GetMenuEntry( rMenuEntry, aMenuItem ); 437 rSubMenu.push_back( aMenuItem ); 438 } 439 } 440 441 } // namespace framework 442