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 <stdio.h>
28
29 //_________________________________________________________________________________________________________________
30 // my own includes
31 //_________________________________________________________________________________________________________________
32 #include <xml/menudocumenthandler.hxx>
33 #include <framework/menuconfiguration.hxx>
34 #include <framework/addonmenu.hxx>
35
36 //_________________________________________________________________________________________________________________
37 // interface includes
38 //_________________________________________________________________________________________________________________
39
40 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
41 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
42 #include <com/sun/star/ui/ItemType.hpp>
43 #include <com/sun/star/ui/ItemStyle.hpp>
44 #include <com/sun/star/beans/PropertyValue.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
46
47 //_________________________________________________________________________________________________________________
48 // other includes
49 //_________________________________________________________________________________________________________________
50 #include <comphelper/processfactory.hxx>
51 #include <rtl/logfile.hxx>
52 #include <comphelper/attributelist.hxx>
53
54 //_________________________________________________________________________________________________________________
55 // defines
56 //_________________________________________________________________________________________________________________
57
58 #define XMLNS_MENU "http://openoffice.org/2001/menu"
59 #define XMLNS_PREFIX "menu:"
60
61 #define ELEMENT_MENUBAR "http://openoffice.org/2001/menu^menubar"
62 #define ELEMENT_MENU "http://openoffice.org/2001/menu^menu"
63 #define ELEMENT_MENUPOPUP "http://openoffice.org/2001/menu^menupopup"
64 #define ELEMENT_MENUITEM "http://openoffice.org/2001/menu^menuitem"
65 #define ELEMENT_MENUSEPARATOR "http://openoffice.org/2001/menu^menuseparator"
66
67 #define ELEMENT_NS_MENUBAR "menu:menubar"
68 #define ELEMENT_NS_MENU "menu:menu"
69 #define ELEMENT_NS_MENUPOPUP "menu:menupopup"
70 #define ELEMENT_NS_MENUITEM "menu:menuitem"
71 #define ELEMENT_NS_MENUSEPARATOR "menu:menuseparator"
72
73 #define ATTRIBUTE_ID "http://openoffice.org/2001/menu^id"
74 #define ATTRIBUTE_LABEL "http://openoffice.org/2001/menu^label"
75 #define ATTRIBUTE_HELPID "http://openoffice.org/2001/menu^helpid"
76 #define ATTRIBUTE_LINEBREAK "http://openoffice.org/2001/menu^linebreak"
77 #define ATTRIBUTE_STYLE "http://openoffice.org/2001/menu^style"
78
79 #define ATTRIBUTE_NS_ID "menu:id"
80 #define ATTRIBUTE_NS_LABEL "menu:label"
81 #define ATTRIBUTE_NS_HELPID "menu:helpid"
82 #define ATTRIBUTE_NS_LINEBREAK "menu:linebreak"
83 #define ATTRIBUTE_NS_STYLE "menu:style"
84
85 #define ATTRIBUTE_XMLNS_MENU "xmlns:menu"
86
87 #define ATTRIBUTE_TYPE_CDATA "CDATA"
88
89 #define MENUBAR_DOCTYPE "<!DOCTYPE menu:menubar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"menubar.dtd\">"
90
91 #define ATTRIBUTE_ITEMSTYLE_TEXT "text"
92 #define ATTRIBUTE_ITEMSTYLE_IMAGE "image"
93 #define ATTRIBUTE_ITEMSTYLE_RADIO "radio"
94
95 // Property names of a menu/menu item ItemDescriptor
96 static const char ITEM_DESCRIPTOR_COMMANDURL[] = "CommandURL";
97 static const char ITEM_DESCRIPTOR_HELPURL[] = "HelpURL";
98 static const char ITEM_DESCRIPTOR_CONTAINER[] = "ItemDescriptorContainer";
99 static const char ITEM_DESCRIPTOR_LABEL[] = "Label";
100 static const char ITEM_DESCRIPTOR_TYPE[] = "Type";
101 static const char ITEM_DESCRIPTOR_STYLE[] = "Style";
102
103 // special popup menus (filled during runtime) must be saved as an empty popup menu or menuitem!!!
104 static const sal_Int32 CMD_PROTOCOL_SIZE = 5;
105 static const char CMD_PROTOCOL[] = ".uno:";
106 static const char ADDDIRECT_CMD[] = ".uno:AddDirect" ;
107 static const char AUTOPILOTMENU_CMD[] = ".uno:AutoPilotMenu" ;
108 static const char FILEMENU_CMD[] = ".uno:Picklist" ;
109 static const char WINDOWMENU_CMD[] = ".uno:WindowList" ;
110
111 //_________________________________________________________________________________________________________________
112 // using namespaces
113 //_________________________________________________________________________________________________________________
114
115 using namespace ::com::sun::star::uno;
116 using namespace ::com::sun::star::lang;
117 using namespace ::com::sun::star::beans;
118 using namespace ::com::sun::star::xml::sax;
119 using namespace ::com::sun::star::container;
120 using namespace ::com::sun::star::ui;
121
122 namespace framework
123 {
124
125 struct MenuStyleItem
126 {
127 sal_Int16 nBit;
128 const char* attrName;
129 };
130
131 MenuStyleItem MenuItemStyles[ ] = {
132 { ::com::sun::star::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE },
133 { ::com::sun::star::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT },
134 { ::com::sun::star::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO }
135 };
136
137
138 sal_Int32 nMenuStyleItemEntries = sizeof( MenuItemStyles ) / sizeof( MenuItemStyles[ 0 ] );
139
ExtractMenuParameters(const Sequence<PropertyValue> rProp,::rtl::OUString & rCommandURL,::rtl::OUString & rLabel,::rtl::OUString & rHelpURL,Reference<XIndexAccess> & rSubMenu,sal_Int16 & rType,sal_Int16 & rStyle)140 static void ExtractMenuParameters( const Sequence< PropertyValue > rProp,
141 ::rtl::OUString& rCommandURL,
142 ::rtl::OUString& rLabel,
143 ::rtl::OUString& rHelpURL,
144 Reference< XIndexAccess >& rSubMenu,
145 sal_Int16& rType,
146 sal_Int16& rStyle )
147 {
148 for ( sal_Int32 i = 0; i < rProp.getLength(); i++ )
149 {
150 if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_COMMANDURL ))
151 {
152 rProp[i].Value >>= rCommandURL;
153 rCommandURL = rCommandURL.intern();
154 }
155 else if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_HELPURL ))
156 {
157 rProp[i].Value >>= rHelpURL;
158 }
159 else if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_CONTAINER ))
160 {
161 rProp[i].Value >>= rSubMenu;
162 }
163 else if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_LABEL ))
164 {
165 rProp[i].Value >>= rLabel;
166 }
167 else if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_TYPE ))
168 {
169 rProp[i].Value >>= rType;
170 }
171 else if ( rProp[i].Name.equalsAscii( ITEM_DESCRIPTOR_STYLE ))
172 {
173 rProp[i].Value >>= rStyle;
174 }
175 }
176 }
177
178
179 // -----------------------------------------------------------------------------
180 // Base class implementation
181
ReadMenuDocumentHandlerBase()182 ReadMenuDocumentHandlerBase::ReadMenuDocumentHandlerBase() :
183 m_xLocator( 0 ),
184 m_xReader( 0 ),
185 m_aType( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_TYPE )),
186 m_aLabel( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_LABEL )),
187 m_aContainer( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_CONTAINER )),
188 m_aHelpURL( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_HELPURL )),
189 m_aCommandURL( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_COMMANDURL )),
190 m_aStyle( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_STYLE ))
191 {
192 }
193
~ReadMenuDocumentHandlerBase()194 ReadMenuDocumentHandlerBase::~ReadMenuDocumentHandlerBase()
195 {
196 }
197
ignorableWhitespace(const::rtl::OUString &)198 void SAL_CALL ReadMenuDocumentHandlerBase::ignorableWhitespace(
199 const ::rtl::OUString& )
200 {
201 }
202
processingInstruction(const::rtl::OUString &,const::rtl::OUString &)203 void SAL_CALL ReadMenuDocumentHandlerBase::processingInstruction(
204 const ::rtl::OUString& /*aTarget*/, const ::rtl::OUString& /*aData*/ )
205 {
206 }
207
setDocumentLocator(const Reference<XLocator> & xLocator)208 void SAL_CALL ReadMenuDocumentHandlerBase::setDocumentLocator(
209 const Reference< XLocator > &xLocator)
210 {
211 m_xLocator = xLocator;
212 }
213
getErrorLineString()214 ::rtl::OUString ReadMenuDocumentHandlerBase::getErrorLineString()
215 {
216 char buffer[32];
217
218 if ( m_xLocator.is() )
219 {
220 snprintf( buffer, sizeof(buffer), "Line: %ld - ", static_cast<long>( m_xLocator->getLineNumber() ));
221 return ::rtl::OUString::createFromAscii( buffer );
222 }
223 else
224 return ::rtl::OUString();
225 }
226
initPropertyCommon(Sequence<PropertyValue> & rProps,const rtl::OUString & rCommandURL,const rtl::OUString & rHelpId,const rtl::OUString & rLabel,sal_Int16 nItemStyleBits)227 void ReadMenuDocumentHandlerBase::initPropertyCommon(
228 Sequence< PropertyValue > &rProps, const rtl::OUString &rCommandURL,
229 const rtl::OUString &rHelpId, const rtl::OUString &rLabel, sal_Int16 nItemStyleBits )
230 {
231 rProps[0].Name = m_aCommandURL;
232 rProps[1].Name = m_aHelpURL;
233 rProps[2].Name = m_aContainer;
234 rProps[3].Name = m_aLabel;
235 rProps[4].Name = m_aStyle;
236 rProps[5].Name = m_aType;
237
238 // Common values
239 rProps[0].Value <<= rCommandURL.intern();
240 rProps[1].Value <<= rHelpId;
241 rProps[2].Value <<= Reference< XIndexContainer >();
242 rProps[3].Value <<= rLabel;
243 rProps[4].Value <<= nItemStyleBits;
244 rProps[5].Value <<= ::com::sun::star::ui::ItemType::DEFAULT;
245 }
246
247 // -----------------------------------------------------------------------------
248
OReadMenuDocumentHandler(const::com::sun::star::uno::Reference<::com::sun::star::lang::XMultiServiceFactory> & xServiceFactory,const Reference<XIndexContainer> & rMenuBarContainer)249 OReadMenuDocumentHandler::OReadMenuDocumentHandler(
250 const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
251 const Reference< XIndexContainer >& rMenuBarContainer )
252 : m_nElementDepth( 0 ),
253 m_bMenuBarMode( sal_False ),
254 m_xMenuBarContainer( rMenuBarContainer ),
255 m_xContainerFactory( rMenuBarContainer, UNO_QUERY ),
256 mxServiceFactory(xServiceFactory)
257 {
258 }
259
260 // #110897#
getServiceFactory()261 const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& OReadMenuDocumentHandler::getServiceFactory()
262 {
263 // #110897#
264 return mxServiceFactory;
265 }
266
~OReadMenuDocumentHandler()267 OReadMenuDocumentHandler::~OReadMenuDocumentHandler()
268 {
269 }
270
271
startDocument(void)272 void SAL_CALL OReadMenuDocumentHandler::startDocument(void)
273 {
274 }
275
276
endDocument(void)277 void SAL_CALL OReadMenuDocumentHandler::endDocument(void)
278 {
279 if ( m_nElementDepth > 0 )
280 {
281 ::rtl::OUString aErrorMessage = getErrorLineString();
282 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "A closing element is missing!" ));
283 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
284 }
285 }
286
287
startElement(const::rtl::OUString & aName,const Reference<XAttributeList> & xAttrList)288 void SAL_CALL OReadMenuDocumentHandler::startElement(
289 const ::rtl::OUString& aName, const Reference< XAttributeList > &xAttrList )
290 {
291 if ( m_bMenuBarMode )
292 {
293 ++m_nElementDepth;
294 m_xReader->startElement( aName, xAttrList );
295 }
296 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUBAR )))
297 {
298 ++m_nElementDepth;
299 m_bMenuBarMode = sal_True;
300
301 // #110897# m_xReader = Reference< XDocumentHandler >( new OReadMenuBarHandler( m_xMenuBarContainer, m_xContainerFactory ));
302 m_xReader = Reference< XDocumentHandler >( new OReadMenuBarHandler( getServiceFactory(), m_xMenuBarContainer, m_xContainerFactory ));
303
304 m_xReader->startDocument();
305 }
306 }
307
308
characters(const rtl::OUString &)309 void SAL_CALL OReadMenuDocumentHandler::characters(const rtl::OUString&)
310 {
311 }
312
313
endElement(const::rtl::OUString & aName)314 void SAL_CALL OReadMenuDocumentHandler::endElement( const ::rtl::OUString& aName )
315 {
316 if ( m_bMenuBarMode )
317 {
318 --m_nElementDepth;
319 m_xReader->endElement( aName );
320 if ( 0 == m_nElementDepth )
321 {
322 m_xReader->endDocument();
323 m_xReader = Reference< XDocumentHandler >();
324 m_bMenuBarMode = sal_False;
325 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUBAR )))
326 {
327 ::rtl::OUString aErrorMessage = getErrorLineString();
328 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menubar expected!" ));
329 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
330 }
331 }
332 }
333 }
334
335
336 // -----------------------------------------------------------------------------
337
338
339 // #110897#
OReadMenuBarHandler(const::com::sun::star::uno::Reference<::com::sun::star::lang::XMultiServiceFactory> & xServiceFactory,const Reference<XIndexContainer> & rMenuBarContainer,const Reference<XSingleComponentFactory> & rFactory)340 OReadMenuBarHandler::OReadMenuBarHandler(
341 const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory,
342 const Reference< XIndexContainer >& rMenuBarContainer,
343 const Reference< XSingleComponentFactory >& rFactory )
344 : m_nElementDepth( 0 ),
345 m_bMenuMode( sal_False ),
346 m_xMenuBarContainer( rMenuBarContainer ),
347 m_xContainerFactory( rFactory ),
348 mxServiceFactory( xServiceFactory )
349 {
350 }
351
352 // #110897#
getServiceFactory()353 const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& OReadMenuBarHandler::getServiceFactory()
354 {
355 // #110897#
356 return mxServiceFactory;
357 }
358
~OReadMenuBarHandler()359 OReadMenuBarHandler::~OReadMenuBarHandler()
360 {
361 }
362
363
startDocument(void)364 void SAL_CALL OReadMenuBarHandler::startDocument(void)
365 {
366 }
367
368
endDocument(void)369 void SAL_CALL OReadMenuBarHandler::endDocument(void)
370 {
371 }
372
373
startElement(const::rtl::OUString & rName,const Reference<XAttributeList> & xAttrList)374 void SAL_CALL OReadMenuBarHandler::startElement(
375 const ::rtl::OUString& rName, const Reference< XAttributeList > &xAttrList )
376 {
377 if ( m_bMenuMode )
378 {
379 ++m_nElementDepth;
380 m_xReader->startElement( rName, xAttrList );
381 }
382 else if ( rName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU )))
383 {
384 ++m_nElementDepth;
385
386 ::rtl::OUString aHelpId;
387 ::rtl::OUString aCommandId;
388 ::rtl::OUString aLabel;
389 sal_Int16 nItemBits(0);
390
391 m_bMenuMode = sal_True;
392
393 // Container must be factory to create sub container
394 Reference< XComponentContext > xComponentContext;
395 Reference< XPropertySet > xProps( ::comphelper::getProcessServiceFactory(), UNO_QUERY );
396 xProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))) >>=
397 xComponentContext;
398
399 Reference< XIndexContainer > xSubItemContainer;
400 if ( m_xContainerFactory.is() )
401 xSubItemContainer = Reference< XIndexContainer >( m_xContainerFactory->createInstanceWithContext( xComponentContext ), UNO_QUERY );
402
403 if ( xSubItemContainer.is() )
404 {
405 // read attributes for menu
406 for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
407 {
408 ::rtl::OUString aName = xAttrList->getNameByIndex( i );
409 ::rtl::OUString aValue = xAttrList->getValueByIndex( i );
410 if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID )))
411 aCommandId = aValue;
412 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL )))
413 aLabel = aValue;
414 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID )))
415 aHelpId = aValue;
416 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_STYLE )))
417 {
418 ::rtl::OUString aTemp( aValue );
419 sal_Int32 nIndex = 0;
420 do
421 {
422 ::rtl::OUString aToken = aTemp.getToken( 0, '+', nIndex );
423 if ( aToken.getLength() > 0 )
424 {
425 if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_TEXT ) ) )
426 nItemBits |= ::com::sun::star::ui::ItemStyle::TEXT;
427 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_IMAGE ) ) )
428 nItemBits |= ::com::sun::star::ui::ItemStyle::ICON;
429 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_RADIO ) ) )
430 nItemBits |= ::com::sun::star::ui::ItemStyle::RADIO_CHECK;
431 }
432 }
433 while ( nIndex >= 0 );
434 }
435 }
436
437 if ( aCommandId.getLength() > 0 )
438 {
439 Sequence< PropertyValue > aSubMenuProp( 6 );
440 initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
441 aSubMenuProp[2].Value <<= xSubItemContainer;
442
443 m_xMenuBarContainer->insertByIndex( m_xMenuBarContainer->getCount(), makeAny( aSubMenuProp ) );
444 }
445 else
446 {
447 ::rtl::OUString aErrorMessage = getErrorLineString();
448 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "attribute id for element menu required!" ));
449 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
450 }
451
452 m_xReader = Reference< XDocumentHandler >( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
453 m_xReader->startDocument();
454 }
455 }
456 else
457 {
458 ::rtl::OUString aErrorMessage = getErrorLineString();
459 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "element menu expected!" ));
460 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
461 }
462 }
463
464
characters(const rtl::OUString &)465 void SAL_CALL OReadMenuBarHandler::characters(const rtl::OUString&)
466 {
467 }
468
469
endElement(const::rtl::OUString & aName)470 void OReadMenuBarHandler::endElement( const ::rtl::OUString& aName )
471 {
472 if ( m_bMenuMode )
473 {
474 --m_nElementDepth;
475 if ( 0 == m_nElementDepth )
476 {
477 m_xReader->endDocument();
478 m_xReader = Reference< XDocumentHandler >();
479 m_bMenuMode = sal_False;
480 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU )))
481 {
482 ::rtl::OUString aErrorMessage = getErrorLineString();
483 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menu expected!" ));
484 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
485 }
486 }
487 else
488 m_xReader->endElement( aName );
489 }
490 }
491
492
493 // -----------------------------------------------------------------------------
494
495
OReadMenuHandler(const Reference<XIndexContainer> & rMenuContainer,const Reference<XSingleComponentFactory> & rFactory)496 OReadMenuHandler::OReadMenuHandler(
497 const Reference< XIndexContainer >& rMenuContainer,
498 const Reference< XSingleComponentFactory >& rFactory ) :
499 m_nElementDepth( 0 ),
500 m_bMenuPopupMode( sal_False ),
501 m_xMenuContainer( rMenuContainer ),
502 m_xContainerFactory( rFactory )
503 {
504 }
505
506
~OReadMenuHandler()507 OReadMenuHandler::~OReadMenuHandler()
508 {
509 }
510
511
startDocument(void)512 void SAL_CALL OReadMenuHandler::startDocument(void)
513 {
514 }
515
516
endDocument(void)517 void SAL_CALL OReadMenuHandler::endDocument(void)
518 {
519 }
520
521
startElement(const::rtl::OUString & aName,const Reference<XAttributeList> & xAttrList)522 void SAL_CALL OReadMenuHandler::startElement(
523 const ::rtl::OUString& aName, const Reference< XAttributeList > &xAttrList )
524 {
525 if ( m_bMenuPopupMode )
526 {
527 ++m_nElementDepth;
528 m_xReader->startElement( aName, xAttrList );
529 }
530 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUPOPUP )))
531 {
532 ++m_nElementDepth;
533 m_bMenuPopupMode = sal_True;
534 m_xReader = Reference< XDocumentHandler >( new OReadMenuPopupHandler( m_xMenuContainer, m_xContainerFactory ));
535 m_xReader->startDocument();
536 }
537 else
538 {
539 ::rtl::OUString aErrorMessage = getErrorLineString();
540 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unknown element found!" ));
541 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
542 }
543 }
544
545
characters(const rtl::OUString &)546 void SAL_CALL OReadMenuHandler::characters(const rtl::OUString&)
547 {
548 }
549
550
endElement(const::rtl::OUString & aName)551 void SAL_CALL OReadMenuHandler::endElement( const ::rtl::OUString& aName )
552 {
553 if ( m_bMenuPopupMode )
554 {
555 --m_nElementDepth;
556 if ( 0 == m_nElementDepth )
557 {
558 m_xReader->endDocument();
559 m_xReader = Reference< XDocumentHandler >();
560 m_bMenuPopupMode = sal_False;
561 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUPOPUP )))
562 {
563 ::rtl::OUString aErrorMessage = getErrorLineString();
564 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menupopup expected!" ));
565 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
566 }
567 }
568 else
569 m_xReader->endElement( aName );
570 }
571 }
572
573
574 // -----------------------------------------------------------------------------
575
576
OReadMenuPopupHandler(const Reference<XIndexContainer> & rMenuContainer,const Reference<XSingleComponentFactory> & rFactory)577 OReadMenuPopupHandler::OReadMenuPopupHandler(
578 const Reference< XIndexContainer >& rMenuContainer,
579 const Reference< XSingleComponentFactory >& rFactory ) :
580 m_nElementDepth( 0 ),
581 m_bMenuMode( sal_False ),
582 m_xMenuContainer( rMenuContainer ),
583 m_xContainerFactory( rFactory ),
584 m_nNextElementExpected( ELEM_CLOSE_NONE )
585 {
586 }
587
588
~OReadMenuPopupHandler()589 OReadMenuPopupHandler::~OReadMenuPopupHandler()
590 {
591 }
592
593
startDocument(void)594 void SAL_CALL OReadMenuPopupHandler::startDocument(void)
595 {
596 }
597
598
endDocument(void)599 void SAL_CALL OReadMenuPopupHandler::endDocument(void)
600 {
601 }
602
startElement(const::rtl::OUString & rName,const Reference<XAttributeList> & xAttrList)603 void SAL_CALL OReadMenuPopupHandler::startElement(
604 const ::rtl::OUString& rName, const Reference< XAttributeList > &xAttrList )
605 {
606 ++m_nElementDepth;
607
608 if ( m_bMenuMode )
609 m_xReader->startElement( rName, xAttrList );
610 else if ( rName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU )))
611 {
612 ::rtl::OUString aHelpId;
613 ::rtl::OUString aCommandId;
614 ::rtl::OUString aLabel;
615 sal_Int16 nItemBits(0);
616
617 m_bMenuMode = sal_True;
618
619 // Container must be factory to create sub container
620 if ( !m_xComponentContext.is() )
621 {
622 const Reference< XPropertySet > xProps( ::comphelper::getProcessServiceFactory(), UNO_QUERY_THROW );
623 m_xComponentContext.set(xProps->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ))), UNO_QUERY_THROW );
624 }
625
626 Reference< XIndexContainer > xSubItemContainer;
627 if ( m_xContainerFactory.is() )
628 xSubItemContainer = Reference< XIndexContainer >( m_xContainerFactory->createInstanceWithContext( m_xComponentContext ), UNO_QUERY );
629
630 // read attributes for menu
631 for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
632 {
633 ::rtl::OUString aName = xAttrList->getNameByIndex( i );
634 ::rtl::OUString aValue = xAttrList->getValueByIndex( i );
635 if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID )))
636 aCommandId = aValue;
637 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL )))
638 aLabel = aValue;
639 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID )))
640 aHelpId = aValue;
641 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_STYLE )))
642 {
643 ::rtl::OUString aTemp( aValue );
644 sal_Int32 nIndex = 0;
645 do
646 {
647 ::rtl::OUString aToken = aTemp.getToken( 0, '+', nIndex );
648 if ( aToken.getLength() > 0 )
649 {
650 if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_TEXT ) ) )
651 nItemBits |= ::com::sun::star::ui::ItemStyle::TEXT;
652 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_IMAGE ) ) )
653 nItemBits |= ::com::sun::star::ui::ItemStyle::ICON;
654 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_RADIO ) ) )
655 nItemBits |= ::com::sun::star::ui::ItemStyle::RADIO_CHECK;
656 }
657 }
658 while ( nIndex >= 0 );
659 }
660
661 }
662
663 if ( aCommandId.getLength() > 0 )
664 {
665 Sequence< PropertyValue > aSubMenuProp( 6 );
666 initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
667 aSubMenuProp[2].Value <<= xSubItemContainer;
668
669 m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), makeAny( aSubMenuProp ) );
670 }
671 else
672 {
673 ::rtl::OUString aErrorMessage = getErrorLineString();
674 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "attribute id for element menu required!" ));
675 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
676 }
677
678 m_xReader = Reference< XDocumentHandler >( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
679 m_xReader->startDocument();
680 }
681 else if ( rName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUITEM )))
682 {
683 ::rtl::OUString aHelpId;
684 ::rtl::OUString aCommandId;
685 ::rtl::OUString aLabel;
686 sal_Int16 nItemBits(0);
687 // read attributes for menu item
688 for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
689 {
690 ::rtl::OUString aName = xAttrList->getNameByIndex( i );
691 ::rtl::OUString aValue = xAttrList->getValueByIndex( i );
692 if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID )))
693 aCommandId = aValue;
694 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL )))
695 aLabel = aValue;
696 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID )))
697 aHelpId = aValue;
698 else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_STYLE )))
699 {
700 ::rtl::OUString aTemp( aValue );
701 sal_Int32 nIndex = 0;
702 do
703 {
704 ::rtl::OUString aToken = aTemp.getToken( 0, '+', nIndex );
705 if ( aToken.getLength() > 0 )
706 {
707 if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_TEXT ) ) )
708 nItemBits |= ::com::sun::star::ui::ItemStyle::TEXT;
709 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_IMAGE ) ) )
710 nItemBits |= ::com::sun::star::ui::ItemStyle::ICON;
711 else if ( aToken.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ITEMSTYLE_RADIO ) ) )
712 nItemBits |= ::com::sun::star::ui::ItemStyle::RADIO_CHECK;
713 }
714 }
715 while ( nIndex >= 0 );
716 }
717
718 }
719
720 if ( aCommandId.getLength() > 0 )
721 {
722 Sequence< PropertyValue > aMenuItem( 6 );
723 initPropertyCommon( aMenuItem, aCommandId, aHelpId, aLabel, nItemBits );
724 aMenuItem[2].Value <<= Reference< XIndexContainer >();
725
726 m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), makeAny( aMenuItem ) );
727 }
728
729 m_nNextElementExpected = ELEM_CLOSE_MENUITEM;
730 }
731 else if ( rName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUSEPARATOR )))
732 {
733 Sequence< PropertyValue > aMenuSeparator( 1 );
734 aMenuSeparator[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ITEM_DESCRIPTOR_TYPE ));
735 aMenuSeparator[0].Value <<= ::com::sun::star::ui::ItemType::SEPARATOR_LINE;
736
737 m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), makeAny( aMenuSeparator ) );
738
739 m_nNextElementExpected = ELEM_CLOSE_MENUSEPARATOR;
740 }
741 else
742 {
743 ::rtl::OUString aErrorMessage = getErrorLineString();
744 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "unknown element found!" ));
745 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
746 }
747 }
748
749
characters(const rtl::OUString &)750 void SAL_CALL OReadMenuPopupHandler::characters(const rtl::OUString&)
751 {
752 }
753
754
endElement(const::rtl::OUString & aName)755 void SAL_CALL OReadMenuPopupHandler::endElement( const ::rtl::OUString& aName )
756 {
757 --m_nElementDepth;
758 if ( m_bMenuMode )
759 {
760 if ( 0 == m_nElementDepth )
761 {
762 m_xReader->endDocument();
763 m_xReader = Reference< XDocumentHandler >();
764 m_bMenuMode = sal_False;
765 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU )))
766 {
767 ::rtl::OUString aErrorMessage = getErrorLineString();
768 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menu expected!" ));
769 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
770 }
771 }
772 else
773 m_xReader->endElement( aName );
774 }
775 else
776 {
777 if ( m_nNextElementExpected == ELEM_CLOSE_MENUITEM )
778 {
779 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUITEM )))
780 {
781 ::rtl::OUString aErrorMessage = getErrorLineString();
782 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menuitem expected!" ));
783 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
784 }
785 }
786 else if ( m_nNextElementExpected == ELEM_CLOSE_MENUSEPARATOR )
787 {
788 if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUSEPARATOR )))
789 {
790 ::rtl::OUString aErrorMessage = getErrorLineString();
791 aErrorMessage += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menuseparator expected!" ));
792 throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
793 }
794 }
795
796 m_nNextElementExpected = ELEM_CLOSE_NONE;
797 }
798 }
799
800
801 // --------------------------------- Write XML ---------------------------------
802
803
OWriteMenuDocumentHandler(const Reference<XIndexAccess> & rMenuBarContainer,const Reference<XDocumentHandler> & rDocumentHandler)804 OWriteMenuDocumentHandler::OWriteMenuDocumentHandler(
805 const Reference< XIndexAccess >& rMenuBarContainer,
806 const Reference< XDocumentHandler >& rDocumentHandler ) :
807 m_xMenuBarContainer( rMenuBarContainer ),
808 m_xWriteDocumentHandler( rDocumentHandler )
809 {
810 ::comphelper::AttributeList* pList = new ::comphelper::AttributeList;
811 m_xEmptyList = Reference< XAttributeList >( (XAttributeList *) pList, UNO_QUERY );
812 m_aAttributeType = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_TYPE_CDATA ));
813 }
814
815
~OWriteMenuDocumentHandler()816 OWriteMenuDocumentHandler::~OWriteMenuDocumentHandler()
817 {
818 }
819
820
WriteMenuDocument()821 void OWriteMenuDocumentHandler::WriteMenuDocument()
822 {
823 ::comphelper::AttributeList* pList = new ::comphelper::AttributeList;
824 Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY );
825
826 m_xWriteDocumentHandler->startDocument();
827
828 // write DOCTYPE line!
829 Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
830 if ( xExtendedDocHandler.is() )
831 {
832 xExtendedDocHandler->unknown( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( MENUBAR_DOCTYPE )) );
833 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
834 }
835
836 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_XMLNS_MENU )),
837 m_aAttributeType,
838 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( XMLNS_MENU )) );
839
840 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )),
841 m_aAttributeType,
842 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "menubar" )) );
843
844 m_xWriteDocumentHandler->startElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUBAR )), pList );
845 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
846
847 WriteMenu( m_xMenuBarContainer );
848
849 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
850 m_xWriteDocumentHandler->endElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUBAR )) );
851 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
852 m_xWriteDocumentHandler->endDocument();
853 }
854
855
WriteMenu(const Reference<XIndexAccess> & rMenuContainer)856 void OWriteMenuDocumentHandler::WriteMenu( const Reference< XIndexAccess >& rMenuContainer )
857 {
858 sal_Int32 nItemCount = rMenuContainer->getCount();
859 sal_Bool bSeparator = sal_False;
860 Any aAny;
861
862 for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
863 {
864 Sequence< PropertyValue > aProps;
865 aAny = rMenuContainer->getByIndex( nItemPos );
866 if ( aAny >>= aProps )
867 {
868 ::rtl::OUString aCommandURL;
869 ::rtl::OUString aLabel;
870 ::rtl::OUString aHelpURL;
871 sal_Int16 nType( ::com::sun::star::ui::ItemType::DEFAULT );
872 sal_Int16 nItemBits( 0 );
873 Reference< XIndexAccess > xSubMenu;
874
875 ExtractMenuParameters( aProps, aCommandURL, aLabel, aHelpURL, xSubMenu, nType, nItemBits );
876 if ( xSubMenu.is() )
877 {
878 if ( aCommandURL.equalsAscii( ADDDIRECT_CMD ) ||
879 aCommandURL.equalsAscii( AUTOPILOTMENU_CMD ))
880 {
881 WriteMenuItem( aCommandURL, aLabel, aHelpURL, nItemBits );
882 bSeparator = sal_False;
883 }
884 else if (( aCommandURL.getLength() > 0 ) && !AddonPopupMenu::IsCommandURLPrefix ( aCommandURL ))
885 {
886 ::comphelper::AttributeList* pListMenu = new ::comphelper::AttributeList;
887 Reference< XAttributeList > xListMenu( (XAttributeList *)pListMenu , UNO_QUERY );
888
889 pListMenu->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )),
890 m_aAttributeType,
891 aCommandURL );
892
893 if ( !( aCommandURL.copy( CMD_PROTOCOL_SIZE ).equalsAscii( CMD_PROTOCOL )))
894 pListMenu->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_LABEL )),
895 m_aAttributeType,
896 aLabel );
897
898 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
899 m_xWriteDocumentHandler->startElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )), xListMenu );
900 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
901 m_xWriteDocumentHandler->startElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )), m_xEmptyList );
902 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
903
904 WriteMenu( xSubMenu );
905
906 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
907 m_xWriteDocumentHandler->endElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )) );
908 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
909 m_xWriteDocumentHandler->endElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )) );
910 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
911 bSeparator = sal_False;
912 }
913 }
914 else
915 {
916 if ( nType == ::com::sun::star::ui::ItemType::DEFAULT )
917 {
918 if ( aCommandURL.getLength() > 0 )
919 {
920 bSeparator = sal_False;
921 WriteMenuItem( aCommandURL, aLabel, aHelpURL, nItemBits );
922 }
923 }
924 else if ( !bSeparator )
925 {
926 // Don't write two separators together
927 WriteMenuSeparator();
928 bSeparator = sal_True;
929 }
930 }
931 }
932 }
933 }
934
935
WriteMenuItem(const::rtl::OUString & aCommandURL,const::rtl::OUString & aLabel,const::rtl::OUString & aHelpURL,sal_Int16 nStyle)936 void OWriteMenuDocumentHandler::WriteMenuItem( const ::rtl::OUString& aCommandURL, const ::rtl::OUString& aLabel, const ::rtl::OUString& aHelpURL, sal_Int16 nStyle )
937 {
938 ::comphelper::AttributeList* pList = new ::comphelper::AttributeList;
939 Reference< XAttributeList > xList( (XAttributeList *) pList , UNO_QUERY );
940
941 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )),
942 m_aAttributeType,
943 aCommandURL );
944
945 if ( aHelpURL.getLength() > 0 )
946 {
947 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_HELPID )),
948 m_aAttributeType,
949 aHelpURL );
950 }
951
952 if (( aLabel.getLength() > 0 ) && !( aCommandURL.copy( CMD_PROTOCOL_SIZE ).equalsAscii( CMD_PROTOCOL )))
953 {
954 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_LABEL )),
955 m_aAttributeType,
956 aLabel );
957 }
958 if (( nStyle > 0 ) && !( aCommandURL.copy( CMD_PROTOCOL_SIZE ).equalsAscii( CMD_PROTOCOL )))
959 {
960 rtl::OUString aValue;
961 MenuStyleItem* pStyle = MenuItemStyles;
962
963 for ( sal_Int32 nIndex = 0; nIndex < nMenuStyleItemEntries; ++nIndex, ++pStyle )
964 {
965 if ( nStyle & pStyle->nBit )
966 {
967 if ( aValue.getLength() )
968 aValue = aValue.concat( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("+") ) );
969 aValue += rtl::OUString::createFromAscii( pStyle->attrName );
970 }
971 }
972 pList->AddAttribute( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_STYLE )),
973 m_aAttributeType,
974 aValue );
975 }
976
977 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
978 m_xWriteDocumentHandler->startElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUITEM )), xList );
979 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
980 m_xWriteDocumentHandler->endElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUITEM )) );
981 }
982
983
WriteMenuSeparator()984 void OWriteMenuDocumentHandler::WriteMenuSeparator()
985 {
986 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
987 m_xWriteDocumentHandler->startElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUSEPARATOR )), m_xEmptyList );
988 m_xWriteDocumentHandler->ignorableWhitespace( ::rtl::OUString() );
989 m_xWriteDocumentHandler->endElement( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUSEPARATOR )) );
990 }
991
992 } // namespace framework
993