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