1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmloff.hxx"
30 #include <xmloff/XMLEventExport.hxx>
31 
32 #ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #endif
35 
36 #ifndef _COM_SUN_STAR_DOCUMENT_XEVENTSSUPPLIER_HPP
37 #include <com/sun/star/document/XEventsSupplier.hpp>
38 #endif
39 
40 #ifndef _COM_SUN_STAR_CONTAINER_XNAMEREPLACE_HPP
41 #include <com/sun/star/container/XNameReplace.hpp>
42 #endif
43 #include <tools/debug.hxx>
44 #include <xmloff/xmlexp.hxx>
45 #include <xmloff/xmltoken.hxx>
46 #include "xmloff/xmlnmspe.hxx"
47 #include <xmloff/nmspmap.hxx>
48 
49 
50 using namespace ::com::sun::star::uno;
51 
52 using std::map;
53 using ::rtl::OUString;
54 using ::com::sun::star::beans::PropertyValue;
55 using ::com::sun::star::document::XEventsSupplier;
56 using ::com::sun::star::container::XNameReplace;
57 using ::com::sun::star::container::XNameAccess;
58 using ::xmloff::token::GetXMLToken;
59 using ::xmloff::token::XML_EVENT_LISTENERS;
60 
61 
62 XMLEventExport::XMLEventExport(SvXMLExport& rExp,
63 						 const XMLEventNameTranslation* pTranslationTable) :
64 	sEventType(RTL_CONSTASCII_USTRINGPARAM("EventType")),
65     rExport(rExp),
66     bExtNamespace(false)
67 {
68 	AddTranslationTable(pTranslationTable);
69 }
70 
71 XMLEventExport::~XMLEventExport()
72 {
73 	// delete all handlers
74 	HandlerMap::iterator aEnd = aHandlerMap.end();
75 	for( HandlerMap::iterator aIter =
76 			 aHandlerMap.begin();
77 		 aIter != aEnd;
78 		 aIter++ )
79 	{
80 		delete aIter->second;
81 	}
82 	aHandlerMap.clear();
83 }
84 
85 void XMLEventExport::AddHandler( const OUString& rName,
86 								 XMLEventExportHandler* pHandler )
87 {
88 	DBG_ASSERT(pHandler != NULL, "Need EventExportHandler");
89 	if (pHandler != NULL)
90 	{
91 		aHandlerMap[rName] = pHandler;
92 	}
93 }
94 
95 void XMLEventExport::AddTranslationTable(
96 	const XMLEventNameTranslation* pTransTable )
97 {
98 	if (NULL != pTransTable)
99 	{
100 		// put translation table into map
101 		for(const XMLEventNameTranslation* pTrans = pTransTable;
102 			pTrans->sAPIName != NULL;
103 			pTrans++)
104 		{
105 			aNameTranslationMap[OUString::createFromAscii(pTrans->sAPIName)] =
106 				XMLEventName(pTrans->nPrefix, pTrans->sXMLName);
107 		}
108 	}
109 	// else? ignore!
110 }
111 
112 void XMLEventExport::Export( Reference<XEventsSupplier> & rSupplier,
113 							 sal_Bool bWhitespace)
114 {
115 	if (rSupplier.is())
116 	{
117 		Reference<XNameAccess> xAccess(rSupplier->getEvents(), UNO_QUERY);
118 		Export(xAccess, bWhitespace);
119 	}
120 	// else: no supplier, no export -> ignore!
121 }
122 
123 void XMLEventExport::Export( Reference<XNameReplace> & rReplace,
124 							 sal_Bool bWhitespace)
125 {
126 	Reference<XNameAccess> xAccess(rReplace, UNO_QUERY);
127 	Export(xAccess, bWhitespace);
128 }
129 
130 void XMLEventExport::Export( Reference<XNameAccess> & rAccess,
131 							 sal_Bool bWhitespace)
132 {
133 	// early out if we don't actually get any events
134 	if (!rAccess.is())
135 	{
136 		return;
137 	}
138 
139 	// have we already processed an element?
140 	sal_Bool bStarted = sal_False;
141 
142 	// iterate over all event types
143 	Sequence<OUString> aNames = rAccess->getElementNames();
144 	sal_Int32 nCount = aNames.getLength();
145 	for(sal_Int32 i = 0; i < nCount; i++)
146 	{
147 		// translate name
148         NameMap::iterator aIter = aNameTranslationMap.find(aNames[i]);
149 		if (aIter != aNameTranslationMap.end())
150 		{
151 			const XMLEventName& rXmlName = aIter->second;
152 
153 			// get PropertyValues for this event
154 			Any aAny = rAccess->getByName( aNames[i] );
155 			Sequence<PropertyValue> aValues;
156 			aAny >>= aValues;
157 
158             // now export the current event
159             ExportEvent( aValues, rXmlName, bWhitespace, bStarted );
160 		}
161 #ifdef DBG_UTIL
162 		else
163 		{
164 			// don't proceed further
165             ::rtl::OString aStr("Unknown event name:" );
166             aStr += ::rtl::OUStringToOString( aNames[i], RTL_TEXTENCODING_UTF8 );
167             DBG_ERROR( aStr.getStr() );
168 		}
169 #endif
170 	}
171 
172 	// close <script:events> element (if it was opened before)
173 	if (bStarted)
174 	{
175 		EndElement(bWhitespace);
176 	}
177 }
178 
179 void XMLEventExport::ExportExt( Reference<XNameAccess> & rAccess,
180                                 sal_Bool bWhitespace )
181 {
182     // set bExtNamespace flag to use XML_NAMESPACE_OFFICE_EXT namespace
183     // for events element (not for child elements)
184     bExtNamespace = true;
185     Export(rAccess, bWhitespace);
186     bExtNamespace = false;          // reset for future Export calls
187 }
188 
189 /// export a singular event and wirte <office:events> container
190 void XMLEventExport::ExportSingleEvent(
191     Sequence<PropertyValue>& rEventValues,
192     const OUString& rApiEventName,
193     sal_Bool bUseWhitespace )
194 {
195     // translate the name
196     NameMap::iterator aIter = aNameTranslationMap.find(rApiEventName);
197     if (aIter != aNameTranslationMap.end())
198 	{
199         const XMLEventName& rXmlName = aIter->second;
200 
201         // export the event ...
202         sal_Bool bStarted = sal_False;
203         ExportEvent( rEventValues, rXmlName, bUseWhitespace, bStarted );
204 
205         // ... and close the container element (if necessary)
206         if (bStarted)
207         {
208             EndElement(bUseWhitespace);
209         }
210     }
211 #ifdef DBG_UTIL
212     else
213     {
214         // don't proceed further
215         ::rtl::OString aStr("Unknown event name:" );
216         aStr += ::rtl::OUStringToOString( rApiEventName, RTL_TEXTENCODING_UTF8 );
217         DBG_ERROR( aStr.getStr() );
218     }
219 #endif
220 }
221 
222 
223 /// export a single event
224 void XMLEventExport::ExportEvent(
225     Sequence<PropertyValue>& rEventValues,
226     const XMLEventName& rXmlEventName,
227     sal_Bool bUseWhitespace,
228     sal_Bool& rExported )
229 {
230     // search for EventType value and then delegate to EventHandler
231     sal_Int32 nValues = rEventValues.getLength();
232     const PropertyValue* pValues = rEventValues.getConstArray();
233 
234     for(sal_Int32 nVal = 0; nVal < nValues; nVal++)
235     {
236         if (sEventType.equals(pValues[nVal].Name))
237         {
238             // found! Now find handler and delegate
239             OUString sType;
240             pValues[nVal].Value >>= sType;
241 
242             if (aHandlerMap.count(sType))
243             {
244                 if (! rExported)
245                 {
246                     // OK, we have't yet exported the enclosing
247                     // element. So we do that now.
248                     rExported = sal_True;
249                     StartElement(bUseWhitespace);
250                 }
251 
252 				OUString aEventQName(
253 					rExport.GetNamespaceMap().GetQNameByKey(
254 							rXmlEventName.m_nPrefix, rXmlEventName.m_aName ) );
255 
256                 // delegate to proper ExportEventHandler
257                 aHandlerMap[sType]->Export(rExport, aEventQName,
258                                            rEventValues, bUseWhitespace);
259             }
260             else
261             {
262                 if (! sType.equalsAsciiL("None", sizeof("None")-1))
263                 {
264                     DBG_ERROR("unknown event type returned by API");
265                     // unknown type -> error (ignore)
266                 }
267                 // else: we ignore None fields
268             }
269 
270             // early out: we don't need to look for another type
271             break;
272         }
273         // else: we only care for EventType -> ignore
274     }
275 }
276 
277 
278 void XMLEventExport::StartElement(sal_Bool bWhitespace)
279 {
280 	if (bWhitespace)
281     {
282 		rExport.IgnorableWhitespace();
283     }
284     sal_uInt16 nNamespace = bExtNamespace ? XML_NAMESPACE_OFFICE_EXT
285                                           : XML_NAMESPACE_OFFICE;
286     rExport.StartElement( nNamespace, XML_EVENT_LISTENERS,
287 						  bWhitespace);
288 }
289 
290 void XMLEventExport::EndElement(sal_Bool bWhitespace)
291 {
292     sal_uInt16 nNamespace = bExtNamespace ? XML_NAMESPACE_OFFICE_EXT
293                                           : XML_NAMESPACE_OFFICE;
294     rExport.EndElement(nNamespace, XML_EVENT_LISTENERS, bWhitespace);
295 	if (bWhitespace)
296 	{
297 		rExport.IgnorableWhitespace();
298 	}
299 }
300 
301 
302 // implement aStandardEventTable (defined in xmlevent.hxx)
303 const XMLEventNameTranslation aStandardEventTable[] =
304 {
305 	{ "OnSelect", 			XML_NAMESPACE_DOM, "select" }, // "on-select"
306 	{ "OnInsertStart",		XML_NAMESPACE_OFFICE, "insert-start" }, // "on-insert-start"
307 	{ "OnInsertDone",		XML_NAMESPACE_OFFICE, "insert-done" }, // "on-insert-done"
308 	{ "OnMailMerge",		XML_NAMESPACE_OFFICE, "mail-merge" }, // "on-mail-merge"
309 	{ "OnAlphaCharInput",	XML_NAMESPACE_OFFICE, "alpha-char-input" }, // "on-alpha-char-input"
310 	{ "OnNonAlphaCharInput",	XML_NAMESPACE_OFFICE, "non-alpha-char-input" }, // "on-non-alpha-char-input"
311 	{ "OnResize",			XML_NAMESPACE_DOM, "resize" }, // "on-resize"
312 	{ "OnMove",				XML_NAMESPACE_OFFICE, "move" }, // "on-move"
313 	{ "OnPageCountChange",	XML_NAMESPACE_OFFICE, "page-count-change" }, // "on-page-count-change"
314 	{ "OnMouseOver",		XML_NAMESPACE_DOM, "mouseover" }, // "on-mouse-over"
315 	{ "OnClick",			XML_NAMESPACE_DOM, "click" }, // "on-click"
316 	{ "OnMouseOut",			XML_NAMESPACE_DOM, "mouseout" }, // "on-mouse-out"
317 	{ "OnLoadError",		XML_NAMESPACE_OFFICE, "load-error" }, // "on-load-error"
318 	{ "OnLoadCancel",		XML_NAMESPACE_OFFICE, "load-cancel" }, // "on-load-cancel"
319 	{ "OnLoadDone",			XML_NAMESPACE_OFFICE, "load-done" }, // "on-load-done"
320 	{ "OnLoad",				XML_NAMESPACE_DOM, "load" }, // "on-load"
321 	{ "OnUnload",			XML_NAMESPACE_DOM, "unload" }, // "on-unload"
322 	{ "OnStartApp",			XML_NAMESPACE_OFFICE, "start-app" }, // "on-start-app"
323 	{ "OnCloseApp",			XML_NAMESPACE_OFFICE, "close-app" }, // "on-close-app"
324 	{ "OnNew",				XML_NAMESPACE_OFFICE, "new" }, // "on-new"
325 	{ "OnSave",				XML_NAMESPACE_OFFICE, "save" }, // "on-save"
326 	{ "OnSaveAs",			XML_NAMESPACE_OFFICE, "save-as" }, // "on-save-as"
327 	{ "OnFocus",			XML_NAMESPACE_DOM, "DOMFocusIn" }, // "on-focus"
328 	{ "OnUnfocus",			XML_NAMESPACE_DOM, "DOMFocusOut" }, // "on-unfocus"
329 	{ "OnPrint",			XML_NAMESPACE_OFFICE, "print" }, // "on-print"
330 	{ "OnError",			XML_NAMESPACE_DOM, "error" }, // "on-error"
331 	{ "OnLoadFinished",		XML_NAMESPACE_OFFICE, "load-finished" }, // "on-load-finished"
332 	{ "OnSaveFinished",		XML_NAMESPACE_OFFICE, "save-finished" }, // "on-save-finished"
333 	{ "OnModifyChanged",	XML_NAMESPACE_OFFICE, "modify-changed" }, // "on-modify-changed"
334 	{ "OnPrepareUnload",	XML_NAMESPACE_OFFICE, "prepare-unload" }, // "on-prepare-unload"
335 	{ "OnNewMail",			XML_NAMESPACE_OFFICE, "new-mail" }, // "on-new-mail"
336 	{ "OnToggleFullscreen",	XML_NAMESPACE_OFFICE, "toggle-fullscreen" }, // "on-toggle-fullscreen"
337 	{ "OnSaveDone", 		XML_NAMESPACE_OFFICE, "save-done" }, // "on-save-done"
338 	{ "OnSaveAsDone",		XML_NAMESPACE_OFFICE, "save-as-done" }, // "on-save-as-done"
339 	{ "OnCopyTo",			XML_NAMESPACE_OFFICE, "copy-to" },
340 	{ "OnCopyToDone",		XML_NAMESPACE_OFFICE, "copy-to-done" },
341 	{ "OnViewCreated",		XML_NAMESPACE_OFFICE, "view-created" },
342 	{ "OnPrepareViewClosing", XML_NAMESPACE_OFFICE, "prepare-view-closing" },
343 	{ "OnViewClosed",       XML_NAMESPACE_OFFICE, "view-close" },
344     { "OnVisAreaChanged",	XML_NAMESPACE_OFFICE, "visarea-changed" }, // "on-visarea-changed"
345     { "OnCreate",           XML_NAMESPACE_OFFICE, "create" },
346     { "OnSaveAsFailed",     XML_NAMESPACE_OFFICE, "save-as-failed" },
347     { "OnSaveFailed",       XML_NAMESPACE_OFFICE, "save-failed" },
348     { "OnCopyToFailed",     XML_NAMESPACE_OFFICE, "copy-to-failed" },
349     { "OnTitleChanged",     XML_NAMESPACE_OFFICE, "title-changed" },
350     { "OnModeChanged",      XML_NAMESPACE_OFFICE, "mode-changed" },
351     { "OnSaveTo",           XML_NAMESPACE_OFFICE, "save-to" },
352     { "OnSaveToDone",       XML_NAMESPACE_OFFICE, "save-to-done" },
353     { "OnSaveToFailed",     XML_NAMESPACE_OFFICE, "save-to-failed" },
354     { "OnSubComponentOpened",   XML_NAMESPACE_OFFICE, "subcomponent-opened" },
355     { "OnSubComponentClosed",   XML_NAMESPACE_OFFICE, "subcomponent-closed" },
356     { "OnStorageChanged",       XML_NAMESPACE_OFFICE, "storage-changed" },
357     { "OnMailMergeFinished",    XML_NAMESPACE_OFFICE, "mail-merge-finished" },
358     { "OnFieldMerge",           XML_NAMESPACE_OFFICE, "field-merge" },
359     { "OnFieldMergeFinished",   XML_NAMESPACE_OFFICE, "field-merge-finished" },
360     { "OnLayoutFinished",       XML_NAMESPACE_OFFICE, "layout-finished" },
361     { "OnDoubleClick",      XML_NAMESPACE_OFFICE, "dblclick" },
362     { "OnRightClick",       XML_NAMESPACE_OFFICE, "contextmenu" },
363     { "OnChange",           XML_NAMESPACE_OFFICE, "content-changed" },
364     { "OnCalculate",        XML_NAMESPACE_OFFICE, "calculated" },
365 
366 	{ NULL, 0, 0 }
367 };
368