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