1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <uielement/menubarmerger.hxx>
21 #include <framework/addonsoptions.hxx>
22 #include <com/sun/star/uno/Sequence.hxx>
23 #include <o3tl/string_view.hxx>
25 using namespace ::com::sun::star
;
27 const char SEPARATOR_STRING
[] = "private:separator";
29 const char16_t MERGECOMMAND_ADDAFTER
[] = u
"AddAfter";
30 const char16_t MERGECOMMAND_ADDBEFORE
[] = u
"AddBefore";
31 const char16_t MERGECOMMAND_REPLACE
[] = u
"Replace";
32 const char16_t MERGECOMMAND_REMOVE
[] = u
"Remove";
34 const char16_t MERGEFALLBACK_ADDPATH
[] = u
"AddPath";
35 const char16_t MERGEFALLBACK_IGNORE
[] = u
"Ignore";
41 Check whether a module identifier is part of a context
42 defined by a colon separated list of module identifier.
47 Describes a context string list where all contexts
48 are delimited by a colon. For more information about
49 the module identifier used as context strings see the
50 IDL description of css::frame::XModuleManager
55 A string describing a module identifier. See IDL
56 description of css::frame::XModuleManager.
59 bool MenuBarMerger::IsCorrectContext(
60 std::u16string_view rContext
, std::u16string_view rModuleIdentifier
)
62 return ( rContext
.empty() || ( rContext
.find( rModuleIdentifier
) != std::u16string_view::npos
));
65 void MenuBarMerger::RetrieveReferencePath(
66 std::u16string_view rReferencePathString
,
67 ::std::vector
< OUString
>& rReferencePath
)
69 const char aDelimiter
= '\\';
71 rReferencePath
.clear();
72 sal_Int32
nIndex( 0 );
75 OUString
aToken( o3tl::getToken(rReferencePathString
, 0, aDelimiter
, nIndex
) );
76 if ( !aToken
.isEmpty() )
77 rReferencePath
.push_back( aToken
);
79 while ( nIndex
>= 0 );
82 ReferencePathInfo
MenuBarMerger::FindReferencePath(
83 const ::std::vector
< OUString
>& rReferencePath
,
87 const sal_uInt32
nCount( rReferencePath
.size() );
89 ReferencePathInfo aResult
;
92 aResult
.pPopupMenu
= nullptr;
95 aResult
.eResult
= RP_MENUITEM_NOT_FOUND
;
99 Menu
* pCurrMenu( pMenu
);
100 RPResultInfo
eResult( RP_OK
);
102 sal_Int32
nLevel( - 1 );
103 sal_uInt16
nPos( MENU_ITEM_NOTFOUND
);
107 OUString
aCmd( rReferencePath
[i
] );
111 // Check last reference path element. Must be a leave (menu item).
112 sal_uInt16 nTmpPos
= FindMenuItem( aCmd
, pCurrMenu
);
113 if ( nTmpPos
!= MENU_ITEM_NOTFOUND
)
115 eResult
= ( nTmpPos
!= MENU_ITEM_NOTFOUND
) ? RP_OK
: RP_MENUITEM_NOT_FOUND
;
119 // Check reference path element. Must be a node (popup menu)!
120 sal_uInt16 nTmpPos
= FindMenuItem( aCmd
, pCurrMenu
);
121 if ( nTmpPos
!= MENU_ITEM_NOTFOUND
)
123 sal_uInt16 nItemId
= pCurrMenu
->GetItemId( nTmpPos
);
124 Menu
* pTmpMenu
= pCurrMenu
->GetPopupMenu( nItemId
);
125 if ( pTmpMenu
!= nullptr )
126 pCurrMenu
= pTmpMenu
;
130 eResult
= RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND
;
134 eResult
= RP_POPUPMENU_NOT_FOUND
;
138 while ((i
< nCount
) && (eResult
== RP_OK
));
140 aResult
.pPopupMenu
= pCurrMenu
;
142 aResult
.nLevel
= nLevel
;
143 aResult
.eResult
= eResult
;
148 sal_uInt16
MenuBarMerger::FindMenuItem( std::u16string_view rCmd
, Menu
const * pCurrMenu
)
150 for ( sal_uInt16 i
= 0; i
< pCurrMenu
->GetItemCount(); i
++ )
152 const sal_uInt16 nItemId
= pCurrMenu
->GetItemId( i
);
155 if ( rCmd
== pCurrMenu
->GetItemCommand( nItemId
) )
160 return MENU_ITEM_NOTFOUND
;
163 bool MenuBarMerger::CreateSubMenu(
166 const OUString
& rModuleIdentifier
,
167 const AddonMenuContainer
& rAddonSubMenu
)
169 const sal_uInt32 nSize
= rAddonSubMenu
.size();
170 for ( sal_uInt32 i
= 0; i
< nSize
; i
++ )
172 const AddonMenuItem
& rMenuItem
= rAddonSubMenu
[i
];
174 if ( IsCorrectContext( rMenuItem
.aContext
, rModuleIdentifier
))
176 if ( rMenuItem
.aURL
== SEPARATOR_STRING
)
178 pSubMenu
->InsertSeparator();
182 pSubMenu
->InsertItem(nItemId
, rMenuItem
.aTitle
);
183 pSubMenu
->SetItemCommand( nItemId
, rMenuItem
.aURL
);
184 if ( !rMenuItem
.aSubMenu
.empty() )
186 VclPtr
<PopupMenu
> pPopupMenu
= VclPtr
<PopupMenu
>::Create();
187 pSubMenu
->SetPopupMenu( nItemId
, pPopupMenu
);
190 CreateSubMenu( pPopupMenu
, nItemId
, rModuleIdentifier
, rMenuItem
.aSubMenu
);
201 bool MenuBarMerger::MergeMenuItems(
204 sal_uInt16 nModIndex
,
206 const OUString
& rModuleIdentifier
,
207 const AddonMenuContainer
& rAddonMenuItems
)
209 sal_uInt16
nIndex( 0 );
210 const sal_uInt32 nSize
= rAddonMenuItems
.size();
211 for ( sal_uInt32 i
= 0; i
< nSize
; i
++ )
213 const AddonMenuItem
& rMenuItem
= rAddonMenuItems
[i
];
215 if ( IsCorrectContext( rMenuItem
.aContext
, rModuleIdentifier
))
217 if ( rMenuItem
.aURL
== SEPARATOR_STRING
)
219 pMenu
->InsertSeparator({}, nPos
+ nModIndex
+ nIndex
);
223 pMenu
->InsertItem(nItemId
, rMenuItem
.aTitle
, MenuItemBits::NONE
, {}, nPos
+ nModIndex
+ nIndex
);
224 pMenu
->SetItemCommand( nItemId
, rMenuItem
.aURL
);
225 if ( !rMenuItem
.aSubMenu
.empty() )
227 VclPtr
<PopupMenu
> pSubMenu
= VclPtr
<PopupMenu
>::Create();
228 pMenu
->SetPopupMenu( nItemId
, pSubMenu
);
231 CreateSubMenu( pSubMenu
, nItemId
, rModuleIdentifier
, rMenuItem
.aSubMenu
);
243 bool MenuBarMerger::ReplaceMenuItem(
247 const OUString
& rModuleIdentifier
,
248 const AddonMenuContainer
& rAddonMenuItems
)
250 // There is no replace available. Therefore we first have to
251 // remove the old menu entry,
252 pMenu
->RemoveItem( nPos
);
254 return MergeMenuItems( pMenu
, nPos
, 0, rItemId
, rModuleIdentifier
, rAddonMenuItems
);
257 bool MenuBarMerger::RemoveMenuItems(
260 std::u16string_view rMergeCommandParameter
)
262 const sal_uInt16
nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter
) ));
263 sal_uInt16 nCount
= std::max( nParam
, sal_uInt16(1) );
266 while (( nPos
< pMenu
->GetItemCount() ) && ( i
< nCount
))
268 pMenu
->RemoveItem( nPos
);
275 bool MenuBarMerger::ProcessMergeOperation(
279 std::u16string_view rMergeCommand
,
280 std::u16string_view rMergeCommandParameter
,
281 const OUString
& rModuleIdentifier
,
282 const AddonMenuContainer
& rAddonMenuItems
)
284 sal_uInt16
nModIndex( 0 );
286 if ( rMergeCommand
== MERGECOMMAND_ADDBEFORE
)
289 return MergeMenuItems( pMenu
, nPos
, nModIndex
, nItemId
, rModuleIdentifier
, rAddonMenuItems
);
291 else if ( rMergeCommand
== MERGECOMMAND_ADDAFTER
)
294 return MergeMenuItems( pMenu
, nPos
, nModIndex
, nItemId
, rModuleIdentifier
, rAddonMenuItems
);
296 else if ( rMergeCommand
== MERGECOMMAND_REPLACE
)
298 return ReplaceMenuItem( pMenu
, nPos
, nItemId
, rModuleIdentifier
, rAddonMenuItems
);
300 else if ( rMergeCommand
== MERGECOMMAND_REMOVE
)
302 return RemoveMenuItems( pMenu
, nPos
, rMergeCommandParameter
);
308 bool MenuBarMerger::ProcessFallbackOperation(
309 const ReferencePathInfo
& aRefPathInfo
,
311 std::u16string_view rMergeCommand
,
312 std::u16string_view rMergeFallback
,
313 const ::std::vector
< OUString
>& rReferencePath
,
314 const std::u16string_view rModuleIdentifier
,
315 const AddonMenuContainer
& rAddonMenuItems
)
317 if (( rMergeFallback
== MERGEFALLBACK_IGNORE
) ||
318 ( rMergeCommand
== MERGECOMMAND_REPLACE
) ||
319 ( rMergeCommand
== MERGECOMMAND_REMOVE
) )
323 else if ( rMergeFallback
== MERGEFALLBACK_ADDPATH
)
325 Menu
* pCurrMenu( aRefPathInfo
.pPopupMenu
);
326 sal_Int32
nLevel( aRefPathInfo
.nLevel
);
327 const sal_Int32
nSize( rReferencePath
.size() );
328 bool bFirstLevel( true );
330 while ( nLevel
< nSize
)
332 if ( nLevel
== nSize
-1 )
334 const sal_uInt32 nCount
= rAddonMenuItems
.size();
335 for ( sal_uInt32 i
= 0; i
< nCount
; ++i
)
337 const AddonMenuItem
& rMenuItem
= rAddonMenuItems
[i
];
338 if ( IsCorrectContext( rMenuItem
.aContext
, rModuleIdentifier
))
340 if ( rMenuItem
.aURL
== SEPARATOR_STRING
)
341 pCurrMenu
->InsertSeparator();
344 pCurrMenu
->InsertItem(rItemId
, rMenuItem
.aTitle
);
345 pCurrMenu
->SetItemCommand( rItemId
, rMenuItem
.aURL
);
353 const OUString
aCmd( rReferencePath
[nLevel
] );
355 VclPtr
<PopupMenu
> pPopupMenu
= VclPtr
<PopupMenu
>::Create();
357 if ( bFirstLevel
&& ( aRefPathInfo
.eResult
== RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND
))
359 // special case: menu item without popup
360 sal_uInt16 nInsPos
= aRefPathInfo
.nPos
;
361 sal_uInt16 nSetItemId
= pCurrMenu
->GetItemId( nInsPos
);
362 pCurrMenu
->SetItemCommand( nSetItemId
, aCmd
);
363 pCurrMenu
->SetPopupMenu( nSetItemId
, pPopupMenu
);
367 // normal case: insert a new item with popup
368 pCurrMenu
->InsertItem(rItemId
, OUString());
369 pCurrMenu
->SetItemCommand( rItemId
, aCmd
);
370 pCurrMenu
->SetPopupMenu( rItemId
, pPopupMenu
);
373 pCurrMenu
= pPopupMenu
;
385 void MenuBarMerger::GetMenuEntry(
386 const uno::Sequence
< beans::PropertyValue
>& rAddonMenuEntry
,
387 AddonMenuItem
& rAddonMenuItem
)
389 // Reset submenu member
390 rAddonMenuItem
.aSubMenu
.clear();
392 for ( const beans::PropertyValue
& rProp
: rAddonMenuEntry
)
394 OUString aMenuEntryPropName
= rProp
.Name
;
395 if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_URL
)
396 rProp
.Value
>>= rAddonMenuItem
.aURL
;
397 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_TITLE
)
398 rProp
.Value
>>= rAddonMenuItem
.aTitle
;
399 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_SUBMENU
)
401 uno::Sequence
< uno::Sequence
< beans::PropertyValue
> > aSubMenu
;
402 rProp
.Value
>>= aSubMenu
;
403 GetSubMenu( aSubMenu
, rAddonMenuItem
.aSubMenu
);
405 else if ( aMenuEntryPropName
== ADDONSMENUITEM_STRING_CONTEXT
)
406 rProp
.Value
>>= rAddonMenuItem
.aContext
;
410 void MenuBarMerger::GetSubMenu(
411 const uno::Sequence
< uno::Sequence
< beans::PropertyValue
> >& rSubMenuEntries
,
412 AddonMenuContainer
& rSubMenu
)
416 const sal_Int32 nCount
= rSubMenuEntries
.getLength();
417 rSubMenu
.reserve(rSubMenu
.size() + nCount
);
418 for ( sal_Int32 i
= 0; i
< nCount
; i
++ )
420 const uno::Sequence
< beans::PropertyValue
>& rMenuEntry
= rSubMenuEntries
[ i
];
422 AddonMenuItem aMenuItem
;
423 GetMenuEntry( rMenuEntry
, aMenuItem
);
424 rSubMenu
.push_back( aMenuItem
);
428 } // namespace framework
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */