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