xref: /trunk/main/framework/source/fwe/classes/addonmenu.cxx (revision 67f7bfb15893aaa2f3b1ee7ec6b966aaaad422fc)
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 //_________________________________________________________________________________________________________________
28 //  my own includes
29 //_________________________________________________________________________________________________________________
30 #include "framework/addonmenu.hxx"
31 #include "framework/addonsoptions.hxx"
32 #include <general.h>
33 #include <macros/debug/assertion.hxx>
34 #include <framework/imageproducer.hxx>
35 #include <framework/menuconfiguration.hxx>
36 #include <services.h>
37 
38 //_________________________________________________________________________________________________________________
39 //  interface includes
40 //_________________________________________________________________________________________________________________
41 #include <com/sun/star/uno/Reference.hxx>
42 #include <com/sun/star/util/URL.hpp>
43 #include <com/sun/star/util/XURLTransformer.hpp>
44 #include <com/sun/star/frame/XModuleManager.hpp>
45 
46 //_________________________________________________________________________________________________________________
47 //  includes of other projects
48 //_________________________________________________________________________________________________________________
49 #include <tools/config.hxx>
50 #include <vcl/svapp.hxx>
51 #include <svtools/menuoptions.hxx>
52 #include <svl/solar.hrc>
53 //_________________________________________________________________________________________________________________
54 //  namespace
55 //_________________________________________________________________________________________________________________
56 
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::lang;
59 using namespace ::com::sun::star::frame;
60 using namespace ::com::sun::star::beans;
61 
62 // Please look at sfx2/inc/sfxsids.hrc the values are defined there. Due to build dependencies
63 // we cannot include the header file.
64 const sal_uInt16 SID_HELPMENU            = (SID_SFX_START + 410);
65 const sal_uInt16 SID_HELP_SUPPORTPAGE = (SID_SFX_START + 1683);
66 
67 namespace framework
68 {
69 
70 AddonMenu::AddonMenu( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& rFrame ) :
71     m_xFrame( rFrame )
72 {
73 }
74 
75 AddonMenu::~AddonMenu()
76 {
77     for ( sal_uInt16 i = 0; i < GetItemCount(); i++ )
78     {
79         if ( GetItemType( i ) != MENUITEM_SEPARATOR )
80         {
81             // delete user attributes created with new!
82             sal_uInt16 nId = GetItemId( i );
83             MenuConfiguration::Attributes* pUserAttributes = (MenuConfiguration::Attributes*)GetUserValue( nId );
84             delete pUserAttributes;
85             delete GetPopupMenu( nId );
86         }
87     }
88 }
89 
90 // ------------------------------------------------------------------------
91 
92 // ------------------------------------------------------------------------
93 // Check if command URL string has the unique prefix to identify addon popup menus
94 sal_Bool AddonPopupMenu::IsCommandURLPrefix( const ::rtl::OUString& aCmdURL )
95 {
96     const char aPrefixCharBuf[] = ADDONSPOPUPMENU_URL_PREFIX_STR;
97 
98     return aCmdURL.matchAsciiL( aPrefixCharBuf, sizeof( aPrefixCharBuf )-1, 0 );
99 }
100 
101 AddonPopupMenu::AddonPopupMenu( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& rFrame ) :
102     AddonMenu( rFrame )
103 {
104 }
105 
106 AddonPopupMenu::~AddonPopupMenu()
107 {
108 }
109 
110 // ------------------------------------------------------------------------
111 
112 static ::rtl::OUString GetModuleIdentifier( const Reference< XMultiServiceFactory >& rServiceManager,
113                                             const Reference< XFrame >& rFrame )
114 {
115     Reference< XModuleManager > xModuleManager( rServiceManager->createInstance( SERVICENAME_MODULEMANAGER ), UNO_QUERY );
116     if ( xModuleManager.is() )
117     {
118         try
119         {
120             return xModuleManager->identify( rFrame );
121         }
122         catch ( Exception& )
123         {
124         }
125     }
126     return ::rtl::OUString();
127 }
128 
129 // ------------------------------------------------------------------------
130 
131 sal_Bool AddonMenuManager::HasAddonMenuElements()
132 {
133     return AddonsOptions().HasAddonsMenu();
134 }
135 
136 sal_Bool AddonMenuManager::HasAddonHelpMenuElements()
137 {
138     return AddonsOptions().HasAddonsHelpMenu();
139 }
140 
141 // Factory method to create different Add-On menu types
142 PopupMenu* AddonMenuManager::CreatePopupMenuType( MenuType eMenuType, const Reference< XFrame >& rFrame )
143 {
144     if ( eMenuType == ADDON_MENU )
145         return new AddonMenu( rFrame );
146     else if ( eMenuType == ADDON_POPUPMENU )
147         return new AddonPopupMenu( rFrame );
148     else
149         return NULL;
150 }
151 
152 // Create the Add-Ons menu
153 AddonMenu* AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame,
154                                               const Reference< XMultiServiceFactory >& rServiceManager )
155 {
156     AddonsOptions aOptions;
157     AddonMenu*  pAddonMenu      = NULL;
158     sal_uInt16      nUniqueMenuId   = ADDONMENU_ITEMID_START;
159 
160     const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
161     if ( rAddonMenuEntries.getLength() > 0 )
162     {
163         pAddonMenu = (AddonMenu *)AddonMenuManager::CreatePopupMenuType( ADDON_MENU, rFrame );
164         ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
165         AddonMenuManager::BuildMenu( pAddonMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
166 
167         // Don't return an empty Add-On menu
168         if ( pAddonMenu->GetItemCount() == 0 )
169         {
170             delete pAddonMenu;
171             pAddonMenu = NULL;
172         }
173     }
174 
175     return pAddonMenu;
176 }
177 
178 // Returns the next insert position from nPos.
179 sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
180 {
181     return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
182 }
183 
184 
185 static sal_uInt16 FindMenuId( Menu* pMenu, const String aCommand )
186 {
187     sal_uInt16 nPos = 0;
188     String aCmd;
189     for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
190     {
191         sal_uInt16 nId = pMenu->GetItemId( nPos );
192         aCmd = pMenu->GetItemCommand( nId );
193         if ( aCmd == aCommand )
194             return nId;
195     }
196 
197     return USHRT_MAX;
198 }
199 
200 
201 // Merge the Add-Ons help menu items into the given menu bar at a defined pos
202 void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
203                                            MenuBar* pMergeMenuBar,
204                                            const Reference< XMultiServiceFactory >& rServiceManager )
205 {
206     if ( pMergeMenuBar )
207     {
208         PopupMenu* pHelpMenu = pMergeMenuBar->GetPopupMenu( SID_HELPMENU );
209         if ( !pHelpMenu )
210         {
211             sal_uInt16 nId = FindMenuId( pMergeMenuBar, String::CreateFromAscii( ".uno:HelpMenu" ));
212             if ( nId != USHRT_MAX )
213                 pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
214         }
215 
216         if ( pHelpMenu )
217         {
218             static const char REFERENCECOMMAND_AFTER[]  = ".uno:HelpSupport";
219             static const char REFERENCECOMMAND_BEFORE[] = ".uno:About";
220 
221             // Add-Ons help menu items should be inserted after the "registration" menu item
222             bool   bAddAfter        = true;
223             sal_uInt16 nItemCount       = pHelpMenu->GetItemCount();
224             sal_uInt16 nSupPos          = pHelpMenu->GetItemPos( SID_HELP_SUPPORTPAGE );
225             sal_uInt16 nInsPos          = nSupPos;
226             sal_uInt16 nInsSepAfterPos  = MENU_APPEND;
227             sal_uInt16 nUniqueMenuId    = ADDONMENU_ITEMID_START;
228             AddonsOptions aOptions;
229 
230             if ( nSupPos == USHRT_MAX )
231             {
232                 // try to detect the online registration dialog menu item with the command URL
233                 sal_uInt16 nId = FindMenuId( pHelpMenu, String::CreateFromAscii( REFERENCECOMMAND_AFTER ));
234                 nSupPos    = pHelpMenu->GetItemPos( nId );
235                 nInsPos    = nSupPos;
236             }
237 
238             if ( nSupPos == USHRT_MAX )
239             {
240                 // second try:
241                 // try to detect the about menu item with the command URL
242                 sal_uInt16 nId = FindMenuId( pHelpMenu, String::CreateFromAscii( REFERENCECOMMAND_BEFORE ));
243                 nSupPos    = pHelpMenu->GetItemPos( nId );
244                 nInsPos    = nSupPos;
245                 bAddAfter  = false;
246             }
247 
248             Sequence< Sequence< PropertyValue > > aAddonSubMenu;
249             const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
250 
251             nInsPos = bAddAfter ? AddonMenuManager::GetNextPos( nInsPos ) : nInsPos;
252             if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MENUITEM_SEPARATOR )
253                 nInsSepAfterPos = nInsPos;
254 
255             ::rtl::OUString aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
256             AddonMenuManager::BuildMenu( pHelpMenu, ADDON_MENU, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
257 
258             if ( pHelpMenu->GetItemCount() > nItemCount )
259             {
260                 if ( nInsSepAfterPos < MENU_APPEND )
261                 {
262                     nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
263                     if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MENUITEM_SEPARATOR )
264                         pHelpMenu->InsertSeparator( nInsSepAfterPos );
265                 }
266                 if ( nSupPos < MENU_APPEND )
267                     pHelpMenu->InsertSeparator( nSupPos+1 );
268                 else
269                     pHelpMenu->InsertSeparator( nItemCount );
270             }
271         }
272     }
273 }
274 
275 // Merge the addon popup menus into the given menu bar at the provided pos.
276 void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
277                                              sal_uInt16               nMergeAtPos,
278                                              MenuBar*             pMergeMenuBar,
279                                              const Reference< XMultiServiceFactory >& rServiceManager )
280 {
281     if ( pMergeMenuBar )
282     {
283         AddonsOptions   aAddonsOptions;
284         sal_uInt16          nInsertPos = nMergeAtPos;
285 
286         ::rtl::OUString                              aTitle;
287         ::rtl::OUString                              aURL;
288         ::rtl::OUString                              aTarget;
289         ::rtl::OUString                              aImageId;
290         ::rtl::OUString                              aContext;
291         Sequence< Sequence< PropertyValue > > aAddonSubMenu;
292         sal_uInt16                                nUniqueMenuId = ADDONMENU_ITEMID_START;
293         ::rtl::OUString                              aModuleIdentifier = GetModuleIdentifier( rServiceManager, rFrame );
294 
295         const Sequence< Sequence< PropertyValue > >&    rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
296         for ( sal_Int32 i = 0; i < rAddonMenuEntries.getLength(); i++ )
297         {
298             AddonMenuManager::GetMenuEntry( rAddonMenuEntries[i],
299                                             aTitle,
300                                             aURL,
301                                             aTarget,
302                                             aImageId,
303                                             aContext,
304                                             aAddonSubMenu );
305             if ( aTitle.getLength() > 0 &&
306                  aURL.getLength() > 0 &&
307                  aAddonSubMenu.getLength() > 0 &&
308                  AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
309             {
310                 sal_uInt16          nId             = nUniqueMenuId++;
311                 AddonPopupMenu* pAddonPopupMenu = (AddonPopupMenu *)AddonMenuManager::CreatePopupMenuType( ADDON_POPUPMENU, rFrame );
312 
313                 AddonMenuManager::BuildMenu( pAddonPopupMenu, ADDON_MENU, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
314 
315                 if ( pAddonPopupMenu->GetItemCount() > 0 )
316                 {
317                     pAddonPopupMenu->SetCommandURL( aURL );
318                     pMergeMenuBar->InsertItem( nId, aTitle, 0, nInsertPos++ );
319                     pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
320 
321                     // Store the command URL into the VCL menu bar for later identification
322                     pMergeMenuBar->SetItemCommand( nId, aURL );
323                 }
324                 else
325                     delete pAddonPopupMenu;
326             }
327         }
328     }
329 }
330 
331 // Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
332 void AddonMenuManager::BuildMenu( PopupMenu*                            pCurrentMenu,
333                                   MenuType                              nSubMenuType,
334                                   sal_uInt16                                nInsPos,
335                                   sal_uInt16&                               nUniqueMenuId,
336                                   Sequence< Sequence< PropertyValue > > aAddonMenuDefinition,
337                                   const Reference< XFrame >&            rFrame,
338                                   const ::rtl::OUString&               rModuleIdentifier )
339 {
340     Sequence< Sequence< PropertyValue > >   aAddonSubMenu;
341     sal_Bool                                    bInsertSeparator    = sal_False;
342     sal_uInt32                                  i                   = 0;
343     sal_uInt32                                  nElements           = 0;
344     sal_uInt32                                  nCount              = aAddonMenuDefinition.getLength();
345     AddonsOptions                           aAddonsOptions;
346 
347     ::rtl::OUString aTitle;
348     ::rtl::OUString aURL;
349     ::rtl::OUString aTarget;
350     ::rtl::OUString aImageId;
351     ::rtl::OUString aContext;
352 
353     for ( i = 0; i < nCount; ++i )
354     {
355         GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aImageId, aContext, aAddonSubMenu );
356 
357         if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( !aTitle.getLength() && !aURL.getLength() ))
358             continue;
359 
360         if ( aURL == ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:separator" )))
361             bInsertSeparator = sal_True;
362         else
363         {
364             PopupMenu* pSubMenu = NULL;
365             if ( aAddonSubMenu.getLength() > 0 )
366             {
367                 pSubMenu = AddonMenuManager::CreatePopupMenuType( nSubMenuType, rFrame );
368                 AddonMenuManager::BuildMenu( pSubMenu, nSubMenuType, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
369 
370                 // Don't create a menu item for an empty sub menu
371                 if ( pSubMenu->GetItemCount() == 0 )
372                 {
373                     delete pSubMenu;
374                     pSubMenu =  NULL;
375                     continue;
376                 }
377             }
378 
379             if ( bInsertSeparator && nElements > 0 )
380             {
381                 // Insert a separator only when we insert a new element afterwards and we
382                 // have already one before us
383                 nElements = 0;
384                 bInsertSeparator = sal_False;
385                 pCurrentMenu->InsertSeparator( nInsPos );
386                 nInsPos = AddonMenuManager::GetNextPos( nInsPos );
387             }
388 
389             sal_uInt16 nId = nUniqueMenuId++;
390             pCurrentMenu->InsertItem( nId, aTitle, 0, nInsPos );
391             nInsPos = AddonMenuManager::GetNextPos( nInsPos );
392 
393             ++nElements;
394 
395             // Store values from configuration to the New and Wizard menu entries to enable
396             // sfx2 based code to support high contrast mode correctly!
397             pCurrentMenu->SetUserValue( nId, sal_uIntPtr( new MenuConfiguration::Attributes( aTarget, aImageId )) );
398             pCurrentMenu->SetItemCommand( nId, aURL );
399 
400             if ( pSubMenu )
401                 pCurrentMenu->SetPopupMenu( nId, pSubMenu );
402         }
403     }
404 }
405 
406 // Retrieve the menu entry property values from a sequence
407 void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
408                                      ::rtl::OUString& rTitle,
409                                      ::rtl::OUString& rURL,
410                                      ::rtl::OUString& rTarget,
411                                      ::rtl::OUString& rImageId,
412                                      ::rtl::OUString& rContext,
413                                      Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
414 {
415     // Reset submenu parameter
416     rAddonSubMenu   = Sequence< Sequence< PropertyValue > >();
417 
418     for ( int i = 0; i < rAddonMenuEntry.getLength(); i++ )
419     {
420         ::rtl::OUString aMenuEntryPropName = rAddonMenuEntry[i].Name;
421         if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_URL )
422             rAddonMenuEntry[i].Value >>= rURL;
423         else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TITLE )
424             rAddonMenuEntry[i].Value >>= rTitle;
425         else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_TARGET )
426             rAddonMenuEntry[i].Value >>= rTarget;
427         else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_IMAGEIDENTIFIER )
428             rAddonMenuEntry[i].Value >>= rImageId;
429         else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_SUBMENU )
430             rAddonMenuEntry[i].Value >>= rAddonSubMenu;
431         else if ( aMenuEntryPropName == ADDONSMENUITEM_PROPERTYNAME_CONTEXT )
432             rAddonMenuEntry[i].Value >>= rContext;
433     }
434 }
435 
436 // Check if the context string matches the provided xModel context
437 sal_Bool AddonMenuManager::IsCorrectContext( const ::rtl::OUString& rModuleIdentifier, const ::rtl::OUString& aContext )
438 {
439     if ( aContext.isEmpty() )
440         return sal_True;
441 
442     if ( !rModuleIdentifier.isEmpty() )
443     {
444         sal_Int32 nIndex = aContext.indexOf( rModuleIdentifier );
445         return ( nIndex >= 0 );
446     }
447 
448     return sal_False;
449 }
450 
451 }
452