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