[Solved] Volatile function in C++
[Solved] Volatile function in C++
Hello,
How do I define a volatile function in a C++ addin??
Thanks,
Saleem
How do I define a volatile function in a C++ addin??
Thanks,
Saleem
Last edited by MrProgrammer on Thu Jan 08, 2026 2:50 am, edited 1 time in total.
Reason: Tagged ✓ [Solved]
Reason: Tagged ✓ [Solved]
OpenOffice 3.4.0
Mac OS X 10.5.8
Mac OS X 10.5.8
Re: Volatile Function
Some info here --
http://wiki.openoffice.org/wiki/Documen ... et_Add-Ins
Ideally I need a full example....
Saleem
http://wiki.openoffice.org/wiki/Documen ... et_Add-Ins
Ideally I need a full example....
Saleem
OpenOffice 3.4.0
Mac OS X 10.5.8
Mac OS X 10.5.8
- Charlie Young
- Volunteer
- Posts: 1559
- Joined: Fri May 14, 2010 1:07 am
Re: Volatile Function
After some hesitation I took this up, since I was curious myself about how this works.
First thing I tried was to just translate the Java example into c++, but after a few hours of frustration I backed off and tried it in Python, and after a considerable struggle I got that working.
Then I went back to the c++ again. I don't want to go over all the stumbling blocks I encountered (though I'll mention a few), but I now have that working, I think. This isn't really useful for much besides illustrating the basic techniques, but since we've got to start somewhere, here goes...
The idl
My first mistake was forgetting that "volatile" is a c++ keyword, and I originally used "volatile" instead of "volresult" in the namespace. Using Volatile as the service name doesn't cause problems, of course, since c++ is case sensitive.
First we need to define a ResultListener. To do OpenOffice listeners in c++, it turns out that helpers are called for, so
then we do
And our listener class then looks like
Then for the result object, that is the ExampleAddInResult class from the Java example, we can do
then
Constructors:
The .reserve(10) isn't terribly important, but I will mention that handling the listeners was ultimately what gave me the most difficulty.
The getResult method is also key. In the Java example, they just do aEvent.Source = this (= self in Python), but c++ is fussier, and one need to explicitly get a Reference<XInterface>. I tried several thing, but eventually lucked onto something that worked.
Moving on, in the Java example the result objects are stored as (key, value) pairs in a hash table. In c++ this is an unordered_map, so in the VolatileImpl class we want (OUStringHash is in class OUString, fortunately).
and since it is static, we need to also declare it outside the definition
The getCounter function itself is then
It seems addResultListener is called automatically, and it is a mistake I made to try and call it explicitly, but a MyResultListener must be declared within the function.
Now what happens when we enter in a cell ("x" is an arbitrary string)
The cell just displays
based on the ResultEvent.Value from the getResult() method, but it will increment whenever the result objects IncrementValue method is called
So I'm making another function to do that
So if we put in a cell
the GETCOUNTER("x") cell will increment x 1, x 2, x 3, etc. each time the INCREMENTCOUNTER is recalculated. Note the the INCREMENTCOUNTER can be on a different sheet and even in a different document.
It also works if called from somewhere else. So if we put in Basic
the GETCOUNTER("x") cell will increment whenever Main is run.
This whole mechanism is intended to update data in real time, of course, and I'm just now starting to look into that part. I'll probably go back to Python for that at first, since I think Python has facilities for reading web data that are much easier to implement than any such thing in c++.
First thing I tried was to just translate the Java example into c++, but after a few hours of frustration I backed off and tried it in Python, and after a considerable struggle I got that working.
Then I went back to the c++ again. I don't want to go over all the stumbling blocks I encountered (though I'll mention a few), but I now have that working, I think. This isn't really useful for much besides illustrating the basic techniques, but since we've got to start somewhere, here goes...
The idl
Code: Select all
#include <com/sun/star/uno/XInterface.idl>
#include <com/sun/star/lang/XInitialization.idl>
#include <com/sun/star/lang/XServiceName.idl>
#include <com/sun/star/lang/XLocalizable.idl>
#include <com/sun/star/sheet/XVolatileResult.idl>
module com {module volresult
{
interface XVolatile
{
string IncrementCounter( [in] string aName );
com::sun::star::sheet::XVolatileResult getCounter( [in] string aName );
};
service Volatile
{
interface XVolatile;
interface com::sun::star::sheet::XVolatileResult;
interface com::sun::star::lang::XInitialization;
interface com::sun::star::lang::XServiceName;
};};
};
First we need to define a ResultListener. To do OpenOffice listeners in c++, it turns out that helpers are called for, so
Code: Select all
#include <cppuhelper/implbase1.hxx>
Code: Select all
typedef ::cppu::WeakImplHelper1< ::com::sun::star::sheet::XResultListener> ResultListenerHelper;
Code: Select all
class MyResultListener: public ResultListenerHelper{
public:
void SAL_CALL modified(const ResultEvent &) throw (::com::sun::star::uno::RuntimeException);
void SAL_CALL disposing(const EventObject &) throw (::com::sun::star::uno::RuntimeException);
};
void SAL_CALL MyResultListener::modified(const ResultEvent & aEvent) throw (::com::sun::star::uno::RuntimeException)
{
return;
}
void SAL_CALL MyResultListener::disposing(const EventObject & aEvent) throw (::com::sun::star::uno::RuntimeException)
{
return;
}
Code: Select all
typedef ::cppu::WeakImplHelper1< ::com::sun::star::sheet::XVolatileResult> VolatileResultHelper;
Code: Select all
class ExampleAddInResult: public VolatileResultHelper {
private:
OUString aName;
long nValue;
vector<Reference< XResultListener >> myListeners;
com::sun::star::sheet::ResultEvent getResult();
public:
ExampleAddInResult(OUString);
ExampleAddInResult();
void SAL_CALL addResultListener(const Reference<XResultListener> & aListener) throw (::com::sun::star::uno::RuntimeException);
void SAL_CALL removeResultListener(const Reference<XResultListener> & aListener) throw (::com::sun::star::uno::RuntimeException);
void incrementValue() {
long i;
++nValue;
com::sun::star::sheet::ResultEvent aEvent = getResult();
for(i = 0;i < myListeners.size(); i++)
myListeners[i]->modified(aEvent);
}
};
void SAL_CALL ExampleAddInResult::addResultListener(const Reference<XResultListener> & aListener) throw (::com::sun::star::uno::RuntimeException) {
myListeners.push_back(aListener);
// immediately notify of initial value
com::sun::star::sheet::ResultEvent aEvent = getResult();
myListeners.back()->modified(aEvent);
}
void SAL_CALL ExampleAddInResult::removeResultListener(const Reference<XResultListener> & aListener) throw (::com::sun::star::uno::RuntimeException) {
myListeners.pop_back();
}
Code: Select all
ExampleAddInResult::ExampleAddInResult(OUString aNewName)
{
aName = aNewName;
myListeners.reserve(10);
nValue = 0;
}
ExampleAddInResult::ExampleAddInResult()
{
aName = OUString();
nValue = 0;
}
The getResult method is also key. In the Java example, they just do aEvent.Source = this (= self in Python), but c++ is fussier, and one need to explicitly get a Reference<XInterface>. I tried several thing, but eventually lucked onto something that worked.
Code: Select all
com::sun::star::sheet::ResultEvent ExampleAddInResult::getResult()
{
com::sun::star::sheet::ResultEvent aEvent;
OUString ou;
Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
aEvent.Value <<= aName + OUString( RTL_CONSTASCII_USTRINGPARAM(" ") ) + ou.valueOf((sal_Int32) nValue);
aEvent.Source = x;
return aEvent;
}
Code: Select all
static unordered_map<OUString,ExampleAddInResult *,OUStringHash> aResults;
and since it is static, we need to also declare it outside the definition
Code: Select all
unordered_map<OUString,ExampleAddInResult *,OUStringHash> VolatileImpl::aResults;
Code: Select all
Reference<XVolatileResult> SAL_CALL VolatileImpl::getCounter( OUString const & aName)
throw (RuntimeException)
{
ExampleAddInResult *aResult;
Reference<XVolatileResult> vResult;
MyResultListener *aListener = new MyResultListener;
Reference<XResultListener> xListener = static_cast< XResultListener * > ( aListener );
if(aResults.count(aName) == 0) {
aResult = new ExampleAddInResult(aName);
aResults[aName] = aResult;
} else {
aResult = aResults[aName];
}
vResult = static_cast< XVolatileResult * > (aResult);
return vResult;
}
Now what happens when we enter in a cell ("x" is an arbitrary string)
Code: Select all
=GETCOUNTER("x")
Code: Select all
x 0
Code: Select all
void incrementValue() {
long i;
++nValue;
com::sun::star::sheet::ResultEvent aEvent = getResult();
for(i = 0;i < myListeners.size(); i++)
myListeners[i]->modified(aEvent);
}
Code: Select all
OUString SAL_CALL VolatileImpl::IncrementCounter( OUString const & aName)
throw (RuntimeException)
{
ExampleAddInResult *aResult;
if(aResults.count(aName) == 0) {
return aName + OUString(RTL_CONSTASCII_USTRINGPARAM(" is null."));
} else {
aResult = aResults[aName];
aResult->incrementValue();
return aName + OUString(RTL_CONSTASCII_USTRINGPARAM(" incremented."));
}
}
Code: Select all
=INCREMENTCOUNTER("x")
the GETCOUNTER("x") cell will increment x 1, x 2, x 3, etc. each time the INCREMENTCOUNTER is recalculated. Note the the INCREMENTCOUNTER can be on a different sheet and even in a different document.
It also works if called from somewhere else. So if we put in Basic
Code: Select all
Sub Main
IncIt("x")
End Sub
Sub IncIt(aName As String)
Dim svc As Object
svc = createUnoService("com.sun.star.sheet.FunctionAccess")
svc.callFunction("com.volresult.my_volatile_implementation.Volatile.IncrementCounter",Array(aName))
End Sub
the GETCOUNTER("x") cell will increment whenever Main is run.
This whole mechanism is intended to update data in real time, of course, and I'm just now starting to look into that part. I'll probably go back to Python for that at first, since I think Python has facilities for reading web data that are much easier to implement than any such thing in c++.
Apache OpenOffice 4.1.1
Windows XP
Windows XP
Re: Volatile Function
Can a volatile function return a 2 dimensional array of data??
Saleem
Saleem
OpenOffice 3.4.0
Mac OS X 10.5.8
Mac OS X 10.5.8
Re: Volatile Function
Hallo
Yes!saleem145 wrote:Can a volatile function return a 2 dimensional array of data??
Saleem
Libreoffice 25.2… on Debian 13 (trixie) (on RaspberryPI5)
Libreoffice 25.8… flatpak on Debian 13 (trixie) (on RaspberryPI5)
Libreoffice 25.8… flatpak on Debian 13 (trixie) (on RaspberryPI5)
- Charlie Young
- Volunteer
- Posts: 1559
- Joined: Fri May 14, 2010 1:07 am
Re: Volatile Function
Just to c0nfirm this.
Modify the getResult() function from above:
Then enter getCounter() as an array formula (Ctrl-Shift-Enter), and the object name and counter are returned in two adjacent cells on a row.
It is worth noting that this only requires modifying the c++, and no changes need to be made to the .idl or the CalcAddin.xcu.
I'll leave it as an exercise to return the entries in a column.
It should be obvious how to extend this to larger arrays, but one consideration might be: is it necessary that the array always have the same dimensions? I'm guessing yes, since my experience is that array formulas are frozen in size after initial entry.
Modify the getResult() function from above:
Code: Select all
com::sun::star::sheet::ResultEvent ExampleAddInResult::getResult()
{
com::sun::star::sheet::ResultEvent aEvent;
Sequence<Sequence<Any>> ResultArray(1);
ResultArray[0] = Sequence<Any>(2);
ResultArray[0][0] <<= aName;
ResultArray[0][1] <<= (sal_Int32) nValue;
aEvent.Value <<= ResultArray;
Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
aEvent.Source = x;
return aEvent;
}
It is worth noting that this only requires modifying the c++, and no changes need to be made to the .idl or the CalcAddin.xcu.
I'll leave it as an exercise to return the entries in a column.
It should be obvious how to extend this to larger arrays, but one consideration might be: is it necessary that the array always have the same dimensions? I'm guessing yes, since my experience is that array formulas are frozen in size after initial entry.
Apache OpenOffice 4.1.1
Windows XP
Windows XP
Re: Volatile Function
=TRANSPOSE(GETCOUNTER("x"))Charlie Young wrote:I'll leave it as an exercise to return the entries in a column.
Please, edit this topic's initial post and add "[Solved]" to the subject line if your problem has been solved.
Ubuntu 18.04 with LibreOffice 6.0, latest OpenOffice and LibreOffice
Ubuntu 18.04 with LibreOffice 6.0, latest OpenOffice and LibreOffice
- Charlie Young
- Volunteer
- Posts: 1559
- Joined: Fri May 14, 2010 1:07 am
Re: Volatile Function
Got meVilleroy wrote:=TRANSPOSE(GETCOUNTER("x"))
(Of course changing the source code is also rather trivial.)
Apache OpenOffice 4.1.1
Windows XP
Windows XP