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 */
IsCorrectContext(const::rtl::OUString & rContext,const::rtl::OUString & rModuleIdentifier)72 bool MenuBarMerger::IsCorrectContext( const ::rtl::OUString& rContext, const ::rtl::OUString& rModuleIdentifier )
73 {
74 return (( rContext.getLength() == 0 ) || ( rContext.indexOf( rModuleIdentifier ) >= 0 ));
75 }
76
RetrieveReferencePath(const::rtl::OUString & rReferencePathString,::std::vector<::rtl::OUString> & rReferencePath)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
FindReferencePath(const::std::vector<::rtl::OUString> & rReferencePath,Menu * pMenu)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
FindMenuItem(const::rtl::OUString & rCmd,Menu * pCurrMenu)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
CreateSubMenu(Menu * pSubMenu,sal_uInt16 & nItemId,const::rtl::OUString & rModuleIdentifier,const AddonMenuContainer & rAddonSubMenu)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
MergeMenuItems(Menu * pMenu,sal_uInt16 nPos,sal_uInt16 nModIndex,sal_uInt16 & nItemId,const::rtl::OUString & rModuleIdentifier,const AddonMenuContainer & rAddonMenuItems)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
ReplaceMenuItem(Menu * pMenu,sal_uInt16 nPos,sal_uInt16 & rItemId,const::rtl::OUString & rModuleIdentifier,const AddonMenuContainer & rAddonMenuItems)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
RemoveMenuItems(Menu * pMenu,sal_uInt16 nPos,const::rtl::OUString & rMergeCommandParameter)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
ProcessMergeOperation(Menu * pMenu,sal_uInt16 nPos,sal_uInt16 & nItemId,const::rtl::OUString & rMergeCommand,const::rtl::OUString & rMergeCommandParameter,const::rtl::OUString & rModuleIdentifier,const AddonMenuContainer & rAddonMenuItems)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
ProcessFallbackOperation(const ReferencePathInfo & aRefPathInfo,sal_uInt16 & rItemId,const::rtl::OUString & rMergeCommand,const::rtl::OUString & rMergeFallback,const::std::vector<::rtl::OUString> & rReferencePath,const::rtl::OUString & rModuleIdentifier,const AddonMenuContainer & rAddonMenuItems)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
GetMenuEntry(const uno::Sequence<beans::PropertyValue> & rAddonMenuEntry,AddonMenuItem & rAddonMenuItem)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
GetSubMenu(const uno::Sequence<uno::Sequence<beans::PropertyValue>> & rSubMenuEntries,AddonMenuContainer & rSubMenu)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