1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <recording/dispatchrecorder.hxx>
21 #include <com/sun/star/frame/DispatchStatement.hpp>
22 #include <com/sun/star/script/Converter.hpp>
24 #include <vcl/svapp.hxx>
25 #include <comphelper/processfactory.hxx>
27 using namespace ::com::sun::star::uno
;
31 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ...
32 #define REM_AS_COMMENT "rem "
34 // XInterface, XTypeProvider, XServiceInfo
36 DEFINE_XSERVICEINFO_MULTISERVICE_2(
39 "com.sun.star.frame.DispatchRecorder",
40 OUString("com.sun.star.comp.framework.DispatchRecorder"))
48 #include <typelib/typedescription.h>
50 void flatten_struct_members(
51 ::std::vector
< Any
> * vec
, void const * data
,
52 typelib_CompoundTypeDescription
* pTD
)
54 if (pTD
->pBaseTypeDescription
)
56 flatten_struct_members( vec
, data
, pTD
->pBaseTypeDescription
);
58 for ( sal_Int32 nPos
= 0; nPos
< pTD
->nMembers
; ++nPos
)
61 Any( static_cast<char const *>(data
) + pTD
->pMemberOffsets
[ nPos
], pTD
->ppTypeRefs
[ nPos
] ) );
65 Sequence
< Any
> make_seq_out_of_struct(
68 Type
const & type
= val
.getValueType();
69 TypeClass eTypeClass
= type
.getTypeClass();
70 if (TypeClass_STRUCT
!= eTypeClass
&& TypeClass_EXCEPTION
!= eTypeClass
)
72 throw RuntimeException(
73 type
.getTypeName() + "is no struct or exception!" );
75 typelib_TypeDescription
* pTD
= 0;
76 TYPELIB_DANGER_GET( &pTD
, type
.getTypeLibType() );
80 throw RuntimeException(
81 "cannot get type descr of type " + type
.getTypeName() );
84 ::std::vector
< Any
> vec
;
85 vec
.reserve( reinterpret_cast<typelib_CompoundTypeDescription
*>(pTD
)->nMembers
); // good guess
86 flatten_struct_members( &vec
, val
.getValue(), reinterpret_cast<typelib_CompoundTypeDescription
*>(pTD
) );
87 TYPELIB_DANGER_RELEASE( pTD
);
88 return Sequence
< Any
>( &vec
[ 0 ], vec
.size() );
91 DispatchRecorder::DispatchRecorder( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
)
93 , m_xConverter(css::script::Converter::create(xContext
))
97 DispatchRecorder::~DispatchRecorder()
102 void SAL_CALL
DispatchRecorder::startRecording( const css::uno::Reference
< css::frame::XFrame
>& ) throw( css::uno::RuntimeException
, std::exception
)
108 void SAL_CALL
DispatchRecorder::recordDispatch( const css::util::URL
& aURL
,
109 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
, std::exception
)
113 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_False
);
114 m_aStatements
.push_back( aStatement
);
117 void SAL_CALL
DispatchRecorder::recordDispatchAsComment( const css::util::URL
& aURL
,
118 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
, std::exception
)
122 // last parameter must be set to true -> it's a comment
123 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_True
);
124 m_aStatements
.push_back( aStatement
);
127 void SAL_CALL
DispatchRecorder::endRecording() throw( css::uno::RuntimeException
, std::exception
)
130 m_aStatements
.clear();
133 OUString SAL_CALL
DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException
, std::exception
)
137 if ( m_aStatements
.empty() )
140 OUStringBuffer aScriptBuffer
;
141 aScriptBuffer
.ensureCapacity(10000);
144 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
145 aScriptBuffer
.appendAscii("rem define variables\n");
146 aScriptBuffer
.appendAscii("dim document as object\n");
147 aScriptBuffer
.appendAscii("dim dispatcher as object\n");
148 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
149 aScriptBuffer
.appendAscii("rem get access to the document\n");
150 aScriptBuffer
.appendAscii("document = ThisComponent.CurrentController.Frame\n");
151 aScriptBuffer
.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
153 std::vector
< com::sun::star::frame::DispatchStatement
>::iterator p
;
154 for ( p
= m_aStatements
.begin(); p
!= m_aStatements
.end(); ++p
)
155 implts_recordMacro( p
->aCommand
, p
->aArgs
, p
->bIsComment
, aScriptBuffer
);
156 OUString sScript
= aScriptBuffer
.makeStringAndClear();
160 void SAL_CALL
DispatchRecorder::AppendToBuffer( css::uno::Any aValue
, OUStringBuffer
& aArgumentBuffer
)
163 if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRUCT
)
165 // structs are recorded as arrays, convert to "Sequence of any"
166 Sequence
< Any
> aSeq
= make_seq_out_of_struct( aValue
);
167 aArgumentBuffer
.appendAscii("Array(");
168 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
170 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
171 if ( nAny
+1 < aSeq
.getLength() )
173 aArgumentBuffer
.appendAscii(",");
176 aArgumentBuffer
.appendAscii(")");
178 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_SEQUENCE
)
180 // convert to "Sequence of any"
181 css::uno::Sequence
< css::uno::Any
> aSeq
;
183 try { aNew
= m_xConverter
->convertTo( aValue
, cppu::UnoType
<css::uno::Sequence
< css::uno::Any
>>::get() ); }
184 catch (const css::uno::Exception
&) {}
187 aArgumentBuffer
.appendAscii("Array(");
188 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
190 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
191 if ( nAny
+1 < aSeq
.getLength() )
193 aArgumentBuffer
.appendAscii(",");
196 aArgumentBuffer
.appendAscii(")");
198 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRING
)
204 // encode non printable characters or '"' by using the CHR$ function
205 if ( !sVal
.isEmpty() )
207 const sal_Unicode
* pChars
= sVal
.getStr();
208 bool bInString
= false;
209 for ( sal_Int32 nChar
=0; nChar
<sVal
.getLength(); nChar
++ )
211 if ( pChars
[nChar
] < 32 || pChars
[nChar
] == '"' )
213 // problematic character detected
216 // close current string
217 aArgumentBuffer
.appendAscii("\"");
222 // if this is not the first character, parts of the string have already been added
223 aArgumentBuffer
.appendAscii("+");
225 // add the character constant
226 aArgumentBuffer
.appendAscii("CHR$(");
227 aArgumentBuffer
.append( (sal_Int32
) pChars
[nChar
] );
228 aArgumentBuffer
.appendAscii(")");
235 // if this is not the first character, parts of the string have already been added
236 aArgumentBuffer
.appendAscii("+");
238 // start a new string
239 aArgumentBuffer
.appendAscii("\"");
243 aArgumentBuffer
.append( pChars
[nChar
] );
249 aArgumentBuffer
.appendAscii("\"");
252 aArgumentBuffer
.appendAscii("\"\"");
254 else if (aValue
.getValueType() == cppu::UnoType
<cppu::UnoCharType
>::get())
256 // character variables are recorded as strings, back conversion must be handled in client code
257 sal_Unicode nVal
= *static_cast<sal_Unicode
const *>(aValue
.getValue());
258 aArgumentBuffer
.appendAscii("\"");
259 if ( (sal_Unicode(nVal
) == '\"') )
261 aArgumentBuffer
.append((sal_Unicode
)nVal
);
262 aArgumentBuffer
.append((sal_Unicode
)nVal
);
263 aArgumentBuffer
.appendAscii("\"");
270 aNew
= m_xConverter
->convertToSimpleType( aValue
, css::uno::TypeClass_STRING
);
272 catch (const css::script::CannotConvertException
&) {}
273 catch (const css::uno::Exception
&) {}
277 if (aValue
.getValueTypeClass() == css::uno::TypeClass_ENUM
)
279 OUString aName
= aValue
.getValueType().getTypeName();
280 aArgumentBuffer
.append( aName
);
281 aArgumentBuffer
.appendAscii(".");
284 aArgumentBuffer
.append(sVal
);
288 void SAL_CALL
DispatchRecorder::implts_recordMacro( const OUString
& aURL
,
289 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
,
290 bool bAsComment
, OUStringBuffer
& aScriptBuffer
)
292 OUStringBuffer
aArgumentBuffer(1000);
294 // this value is used to name the arrays of aArgumentBuffer
295 sArrayName
= "args" + OUString::number(m_nRecordingID
);
297 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
299 sal_Int32 nLength
= lArguments
.getLength();
300 sal_Int32 nValidArgs
= 0;
301 for( sal_Int32 i
=0; i
<nLength
; ++i
)
303 if(!lArguments
[i
].Value
.hasValue())
306 OUStringBuffer
sValBuffer(100);
309 AppendToBuffer(lArguments
[i
].Value
, sValBuffer
);
311 catch(const css::uno::Exception
&)
313 sValBuffer
.setLength(0);
315 if (sValBuffer
.isEmpty())
321 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
322 aArgumentBuffer
.append (sArrayName
);
323 aArgumentBuffer
.appendAscii("(");
324 aArgumentBuffer
.append (nValidArgs
);
325 aArgumentBuffer
.appendAscii(").Name = \"");
326 aArgumentBuffer
.append (lArguments
[i
].Name
);
327 aArgumentBuffer
.appendAscii("\"\n");
331 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
332 aArgumentBuffer
.append (sArrayName
);
333 aArgumentBuffer
.appendAscii("(");
334 aArgumentBuffer
.append (nValidArgs
);
335 aArgumentBuffer
.appendAscii(").Value = ");
336 aArgumentBuffer
.append (sValBuffer
.makeStringAndClear());
337 aArgumentBuffer
.appendAscii("\n");
343 // if aArgumentBuffer exist - pack it into the aScriptBuffer
347 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
348 aScriptBuffer
.appendAscii("dim ");
349 aScriptBuffer
.append (sArrayName
);
350 aScriptBuffer
.appendAscii("(");
351 aScriptBuffer
.append ((sal_Int32
)(nValidArgs
-1)); // 0 based!
352 aScriptBuffer
.appendAscii(") as new com.sun.star.beans.PropertyValue\n");
353 aScriptBuffer
.append (aArgumentBuffer
.makeStringAndClear());
354 aScriptBuffer
.appendAscii("\n");
357 // add code for dispatches
359 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
360 aScriptBuffer
.appendAscii("dispatcher.executeDispatch(document, \"");
361 aScriptBuffer
.append (aURL
);
362 aScriptBuffer
.appendAscii("\", \"\", 0, ");
364 aScriptBuffer
.appendAscii("Array()");
367 aScriptBuffer
.append( sArrayName
.getStr() );
368 aScriptBuffer
.appendAscii("()");
370 aScriptBuffer
.appendAscii(")\n\n");
377 com::sun::star::uno::Type SAL_CALL
DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException
, std::exception
)
379 return cppu::UnoType
<com::sun::star::frame::DispatchStatement
>::get();
382 sal_Bool SAL_CALL
DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException
, std::exception
)
384 return (! m_aStatements
.empty());
387 sal_Int32 SAL_CALL
DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException
, std::exception
)
389 return m_aStatements
.size();
392 com::sun::star::uno::Any SAL_CALL
DispatchRecorder::getByIndex(sal_Int32 idx
) throw (::com::sun::star::lang::IndexOutOfBoundsException
, ::com::sun::star::lang::WrappedTargetException
, ::com::sun::star::uno::RuntimeException
, std::exception
)
394 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
395 throw com::sun::star::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" );
398 Any
element(&m_aStatements
[idx
],
399 cppu::UnoType
<com::sun::star::frame::DispatchStatement
>::get());
404 void SAL_CALL
DispatchRecorder::replaceByIndex(sal_Int32 idx
, const com::sun::star::uno::Any
& element
) throw (::com::sun::star::lang::IllegalArgumentException
, ::com::sun::star::lang::IndexOutOfBoundsException
, ::com::sun::star::lang::WrappedTargetException
, ::com::sun::star::uno::RuntimeException
, std::exception
)
406 if (element
.getValueType() !=
407 cppu::UnoType
<com::sun::star::frame::DispatchStatement
>::get()) {
408 throw com::sun::star::lang::IllegalArgumentException(
409 "Illegal argument in dispatch recorder",
410 Reference
< XInterface
>(), 2 );
413 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
414 throw com::sun::star::lang::IndexOutOfBoundsException(
415 "Dispatch recorder out of bounds" );
419 com::sun::star::frame::DispatchStatement
const *pStatement
;
421 pStatement
= static_cast<com::sun::star::frame::DispatchStatement
const *>(element
.getValue());
423 com::sun::star::frame::DispatchStatement
aStatement(
424 pStatement
->aCommand
,
428 pStatement
->bIsComment
);
430 m_aStatements
[idx
] = aStatement
;
433 } // namespace framework
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */