LibreOffice Module framework (master) 1
menubarmerger.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
22#include <com/sun/star/uno/Sequence.hxx>
23#include <o3tl/string_view.hxx>
24
25using namespace ::com::sun::star;
26
27const char SEPARATOR_STRING[] = "private:separator";
28
29const char16_t MERGECOMMAND_ADDAFTER[] = u"AddAfter";
30const char16_t MERGECOMMAND_ADDBEFORE[] = u"AddBefore";
31const char16_t MERGECOMMAND_REPLACE[] = u"Replace";
32const char16_t MERGECOMMAND_REMOVE[] = u"Remove";
33
34const char16_t MERGEFALLBACK_ADDPATH[] = u"AddPath";
35const char16_t MERGEFALLBACK_IGNORE[] = u"Ignore";
36
37namespace framework
38{
39
60 std::u16string_view rContext, std::u16string_view rModuleIdentifier )
61{
62 return ( rContext.empty() || ( rContext.find( rModuleIdentifier ) != std::u16string_view::npos ));
63}
64
66 std::u16string_view rReferencePathString,
67 ::std::vector< OUString >& rReferencePath )
68{
69 const char aDelimiter = '\\';
70
71 rReferencePath.clear();
72 sal_Int32 nIndex( 0 );
73 do
74 {
75 OUString aToken( o3tl::getToken(rReferencePathString, 0, aDelimiter, nIndex ) );
76 if ( !aToken.isEmpty() )
77 rReferencePath.push_back( aToken );
78 }
79 while ( nIndex >= 0 );
80}
81
82ReferencePathInfo MenuBarMerger::FindReferencePath(
83 const ::std::vector< OUString >& rReferencePath,
84 Menu* pMenu )
85{
86 sal_uInt32 i( 0 );
87 const sal_uInt32 nCount( rReferencePath.size() );
88
89 ReferencePathInfo aResult;
90 if ( !nCount )
91 {
92 aResult.pPopupMenu = nullptr;
93 aResult.nPos = 0;
94 aResult.nLevel = -1;
95 aResult.eResult = RP_MENUITEM_NOT_FOUND;
96 return aResult;
97 }
98
99 Menu* pCurrMenu( pMenu );
100 RPResultInfo eResult( RP_OK );
101
102 sal_Int32 nLevel( - 1 );
103 sal_uInt16 nPos( MENU_ITEM_NOTFOUND );
104 do
105 {
106 ++nLevel;
107 OUString aCmd( rReferencePath[i] );
108
109 if ( i == nCount-1 )
110 {
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 )
114 nPos = nTmpPos;
115 eResult = ( nTmpPos != MENU_ITEM_NOTFOUND ) ? RP_OK : RP_MENUITEM_NOT_FOUND;
116 }
117 else
118 {
119 // Check reference path element. Must be a node (popup menu)!
120 sal_uInt16 nTmpPos = FindMenuItem( aCmd, pCurrMenu );
121 if ( nTmpPos != MENU_ITEM_NOTFOUND )
122 {
123 sal_uInt16 nItemId = pCurrMenu->GetItemId( nTmpPos );
124 Menu* pTmpMenu = pCurrMenu->GetPopupMenu( nItemId );
125 if ( pTmpMenu != nullptr )
126 pCurrMenu = pTmpMenu;
127 else
128 {
129 nPos = nTmpPos;
131 }
132 }
133 else
134 eResult = RP_POPUPMENU_NOT_FOUND;
135 }
136 i++;
137 }
138 while ((i < nCount) && (eResult == RP_OK));
139
140 aResult.pPopupMenu = pCurrMenu;
141 aResult.nPos = nPos;
142 aResult.nLevel = nLevel;
143 aResult.eResult = eResult;
144
145 return aResult;
146}
147
148sal_uInt16 MenuBarMerger::FindMenuItem( std::u16string_view rCmd, Menu const * pCurrMenu )
149{
150 for ( sal_uInt16 i = 0; i < pCurrMenu->GetItemCount(); i++ )
151 {
152 const sal_uInt16 nItemId = pCurrMenu->GetItemId( i );
153 if ( nItemId > 0 )
154 {
155 if ( rCmd == pCurrMenu->GetItemCommand( nItemId ) )
156 return i;
157 }
158 }
159
160 return MENU_ITEM_NOTFOUND;
161}
162
164 Menu* pSubMenu,
165 sal_uInt16& nItemId,
166 const OUString& rModuleIdentifier,
167 const AddonMenuContainer& rAddonSubMenu )
168{
169 const sal_uInt32 nSize = rAddonSubMenu.size();
170 for ( sal_uInt32 i = 0; i < nSize; i++ )
171 {
172 const AddonMenuItem& rMenuItem = rAddonSubMenu[i];
173
174 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
175 {
176 if ( rMenuItem.aURL == SEPARATOR_STRING )
177 {
178 pSubMenu->InsertSeparator();
179 }
180 else
181 {
182 pSubMenu->InsertItem(nItemId, rMenuItem.aTitle);
183 pSubMenu->SetItemCommand( nItemId, rMenuItem.aURL );
184 if ( !rMenuItem.aSubMenu.empty() )
185 {
187 pSubMenu->SetPopupMenu( nItemId, pPopupMenu );
188 ++nItemId;
189
190 CreateSubMenu( pPopupMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
191 }
192 else
193 ++nItemId;
194 }
195 }
196 }
197
198 return true;
199}
200
202 Menu* pMenu,
203 sal_uInt16 nPos,
204 sal_uInt16 nModIndex,
205 sal_uInt16& nItemId,
206 const OUString& rModuleIdentifier,
207 const AddonMenuContainer& rAddonMenuItems )
208{
209 sal_uInt16 nIndex( 0 );
210 const sal_uInt32 nSize = rAddonMenuItems.size();
211 for ( sal_uInt32 i = 0; i < nSize; i++ )
212 {
213 const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
214
215 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
216 {
217 if ( rMenuItem.aURL == SEPARATOR_STRING )
218 {
219 pMenu->InsertSeparator({}, nPos + nModIndex + nIndex);
220 }
221 else
222 {
223 pMenu->InsertItem(nItemId, rMenuItem.aTitle, MenuItemBits::NONE, {}, nPos + nModIndex + nIndex);
224 pMenu->SetItemCommand( nItemId, rMenuItem.aURL );
225 if ( !rMenuItem.aSubMenu.empty() )
226 {
228 pMenu->SetPopupMenu( nItemId, pSubMenu );
229 ++nItemId;
230
231 CreateSubMenu( pSubMenu, nItemId, rModuleIdentifier, rMenuItem.aSubMenu );
232 }
233 else
234 ++nItemId;
235 }
236 ++nIndex;
237 }
238 }
239
240 return true;
241}
242
244 Menu* pMenu,
245 sal_uInt16 nPos,
246 sal_uInt16& rItemId,
247 const OUString& rModuleIdentifier,
248 const AddonMenuContainer& rAddonMenuItems )
249{
250 // There is no replace available. Therefore we first have to
251 // remove the old menu entry,
252 pMenu->RemoveItem( nPos );
253
254 return MergeMenuItems( pMenu, nPos, 0, rItemId, rModuleIdentifier, rAddonMenuItems );
255}
256
258 Menu* pMenu,
259 sal_uInt16 nPos,
260 std::u16string_view rMergeCommandParameter )
261{
262 const sal_uInt16 nParam( sal_uInt16( o3tl::toInt32(rMergeCommandParameter) ));
263 sal_uInt16 nCount = std::max( nParam, sal_uInt16(1) );
264
265 sal_uInt16 i = 0;
266 while (( nPos < pMenu->GetItemCount() ) && ( i < nCount ))
267 {
268 pMenu->RemoveItem( nPos );
269 ++i;
270 }
271
272 return true;
273}
274
276 Menu* pMenu,
277 sal_uInt16 nPos,
278 sal_uInt16& nItemId,
279 std::u16string_view rMergeCommand,
280 std::u16string_view rMergeCommandParameter,
281 const OUString& rModuleIdentifier,
282 const AddonMenuContainer& rAddonMenuItems )
283{
284 sal_uInt16 nModIndex( 0 );
285
286 if ( rMergeCommand == MERGECOMMAND_ADDBEFORE )
287 {
288 nModIndex = 0;
289 return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
290 }
291 else if ( rMergeCommand == MERGECOMMAND_ADDAFTER )
292 {
293 nModIndex = 1;
294 return MergeMenuItems( pMenu, nPos, nModIndex, nItemId, rModuleIdentifier, rAddonMenuItems );
295 }
296 else if ( rMergeCommand == MERGECOMMAND_REPLACE )
297 {
298 return ReplaceMenuItem( pMenu, nPos, nItemId, rModuleIdentifier, rAddonMenuItems );
299 }
300 else if ( rMergeCommand == MERGECOMMAND_REMOVE )
301 {
302 return RemoveMenuItems( pMenu, nPos, rMergeCommandParameter );
303 }
304
305 return false;
306}
307
309 const ReferencePathInfo& aRefPathInfo,
310 sal_uInt16& rItemId,
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 )
316{
317 if (( rMergeFallback == MERGEFALLBACK_IGNORE ) ||
318 ( rMergeCommand == MERGECOMMAND_REPLACE ) ||
319 ( rMergeCommand == MERGECOMMAND_REMOVE ) )
320 {
321 return true;
322 }
323 else if ( rMergeFallback == MERGEFALLBACK_ADDPATH )
324 {
325 Menu* pCurrMenu( aRefPathInfo.pPopupMenu );
326 sal_Int32 nLevel( aRefPathInfo.nLevel );
327 const sal_Int32 nSize( rReferencePath.size() );
328 bool bFirstLevel( true );
329
330 while ( nLevel < nSize )
331 {
332 if ( nLevel == nSize-1 )
333 {
334 const sal_uInt32 nCount = rAddonMenuItems.size();
335 for ( sal_uInt32 i = 0; i < nCount; ++i )
336 {
337 const AddonMenuItem& rMenuItem = rAddonMenuItems[i];
338 if ( IsCorrectContext( rMenuItem.aContext, rModuleIdentifier ))
339 {
340 if ( rMenuItem.aURL == SEPARATOR_STRING )
341 pCurrMenu->InsertSeparator();
342 else
343 {
344 pCurrMenu->InsertItem(rItemId, rMenuItem.aTitle);
345 pCurrMenu->SetItemCommand( rItemId, rMenuItem.aURL );
346 ++rItemId;
347 }
348 }
349 }
350 }
351 else
352 {
353 const OUString aCmd( rReferencePath[nLevel] );
354
356
357 if ( bFirstLevel && ( aRefPathInfo.eResult == RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND ))
358 {
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 );
364 }
365 else
366 {
367 // normal case: insert a new item with popup
368 pCurrMenu->InsertItem(rItemId, OUString());
369 pCurrMenu->SetItemCommand( rItemId, aCmd );
370 pCurrMenu->SetPopupMenu( rItemId, pPopupMenu );
371 }
372
373 pCurrMenu = pPopupMenu;
374 ++rItemId;
375 bFirstLevel = false;
376 }
377 ++nLevel;
378 }
379 return true;
380 }
381
382 return false;
383}
384
386 const uno::Sequence< beans::PropertyValue >& rAddonMenuEntry,
387 AddonMenuItem& rAddonMenuItem )
388{
389 // Reset submenu member
390 rAddonMenuItem.aSubMenu.clear();
391
392 for ( const beans::PropertyValue& rProp : rAddonMenuEntry )
393 {
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 )
400 {
401 uno::Sequence< uno::Sequence< beans::PropertyValue > > aSubMenu;
402 rProp.Value >>= aSubMenu;
403 GetSubMenu( aSubMenu, rAddonMenuItem.aSubMenu );
404 }
405 else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
406 rProp.Value >>= rAddonMenuItem.aContext;
407 }
408}
409
411 const uno::Sequence< uno::Sequence< beans::PropertyValue > >& rSubMenuEntries,
412 AddonMenuContainer& rSubMenu )
413{
414 rSubMenu.clear();
415
416 const sal_Int32 nCount = rSubMenuEntries.getLength();
417 rSubMenu.reserve(rSubMenu.size() + nCount);
418 for ( sal_Int32 i = 0; i < nCount; i++ )
419 {
420 const uno::Sequence< beans::PropertyValue >& rMenuEntry = rSubMenuEntries[ i ];
421
422 AddonMenuItem aMenuItem;
423 GetMenuEntry( rMenuEntry, aMenuItem );
424 rSubMenu.push_back( aMenuItem );
425 }
426}
427
428} // namespace framework
429
430/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr OUStringLiteral ADDONSMENUITEM_STRING_URL
constexpr OUStringLiteral ADDONSMENUITEM_STRING_SUBMENU
constexpr OUStringLiteral ADDONSMENUITEM_STRING_CONTEXT
constexpr OUStringLiteral ADDONSMENUITEM_STRING_TITLE
void InsertSeparator(const OUString &rIdent={}, sal_uInt16 nPos=MENU_APPEND)
OUString GetItemCommand(sal_uInt16 nItemId) const
sal_uInt16 GetItemId(sal_uInt16 nPos) const
void SetItemCommand(sal_uInt16 nItemId, const OUString &rCommand)
PopupMenu * GetPopupMenu(sal_uInt16 nItemId) const
void RemoveItem(sal_uInt16 nPos)
void SetPopupMenu(sal_uInt16 nItemId, PopupMenu *pMenu)
void InsertItem(sal_uInt16 nItemId, const OUString &rStr, MenuItemBits nItemBits=MenuItemBits::NONE, const OUString &rIdent={}, sal_uInt16 nPos=MENU_APPEND)
sal_uInt16 GetItemCount() const
static VclPtr< reference_type > Create(Arg &&... arg)
int nCount
float u
sal_Int32 nIndex
sal_uInt16 nPos
constexpr sal_uInt16 MENU_ITEM_NOTFOUND
const char16_t MERGEFALLBACK_IGNORE[]
const char16_t MERGECOMMAND_ADDBEFORE[]
const char16_t MERGEFALLBACK_ADDPATH[]
const char16_t MERGECOMMAND_REPLACE[]
const char16_t MERGECOMMAND_REMOVE[]
const char SEPARATOR_STRING[]
const char16_t MERGECOMMAND_ADDAFTER[]
bool ProcessFallbackOperation(const ReferencePathInfo &aRefPathInfo, sal_uInt16 &rItemId, std::u16string_view rMergeCommand, std::u16string_view rMergeFallback, const ::std::vector< OUString > &rReferencePath, std::u16string_view rModuleIdentifier, const AddonMenuContainer &rAddonMenuItems)
void GetSubMenu(const css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > &rSubMenuEntries, AddonMenuContainer &rSubMenu)
bool ProcessMergeOperation(Menu *pMenu, sal_uInt16 nPos, sal_uInt16 &rItemId, std::u16string_view rMergeCommand, std::u16string_view rMergeCommandParameter, const OUString &rModuleIdentifier, const AddonMenuContainer &rAddonMenuItems)
bool MergeMenuItems(Menu *pMenu, sal_uInt16 nPos, sal_uInt16 nModIndex, sal_uInt16 &rItemId, const OUString &rModuleIdentifier, const AddonMenuContainer &rAddonMenuItems)
sal_uInt16 FindMenuItem(std::u16string_view rCmd, Menu const *pMenu)
bool ReplaceMenuItem(Menu *pMenu, sal_uInt16 nPos, sal_uInt16 &rItemId, const OUString &rModuleIdentifier, const AddonMenuContainer &rAddonMenuItems)
void GetMenuEntry(const css::uno::Sequence< css::beans::PropertyValue > &rAddonMenuEntry, AddonMenuItem &aAddonMenu)
bool RemoveMenuItems(Menu *pMenu, sal_uInt16 nPos, std::u16string_view rMergeCommandParameter)
bool IsCorrectContext(std::u16string_view aContext, std::u16string_view aModuleIdentifier)
Check whether a module identifier is part of a context defined by a colon separated list of module id...
void RetrieveReferencePath(std::u16string_view, std::vector< OUString > &aReferencePath)
bool CreateSubMenu(Menu *pSubMenu, sal_uInt16 &nItemId, const OUString &rModuleIdentifier, const AddonMenuContainer &rAddonSubMenu)
ReferencePathInfo FindReferencePath(const std::vector< OUString > &aReferencePath, Menu *pMenu)
const char16_t MERGECOMMAND_REPLACE[]
::std::vector< AddonMenuItem > AddonMenuContainer
const char16_t MERGEFALLBACK_IGNORE[]
@ RP_MENUITEM_NOT_FOUND
@ RP_POPUPMENU_NOT_FOUND
@ RP_MENUITEM_INSTEAD_OF_POPUPMENU_FOUND
const char16_t MERGECOMMAND_ADDAFTER[]
const char16_t MERGECOMMAND_REMOVE[]
const char16_t MERGECOMMAND_ADDBEFORE[]
int i
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
AddonMenuContainer aSubMenu