We will be migrating from Ask to Discourse on the first week of August, read the details here

# How to programmatically get/set a form control's events

Howdy, was wondering how I could get / set the macro assigned to a particular form control event, say, mouse press.

Am trying to do this without the gui, thus avoiding going into design mode, right-clicking on the control, clicking "Control Properties" then "Events", then scrolling through the event entry to see what it's running.

Using XRay I've looked at the object reference for Buttons (com.sun.star.form.OButtonControl) and Button Models (com.sun.star.form.OButtonModel) but don't seem to see anything relevant there.

Did find something in the Document object (ScModelObj has a getEvents method as well as an Events property property object containing getByName and replaceByName methods), so was thinking there would be something similar for form controls.

As to why, sheer laziness. When developing I have a "reminder" routine which displays a message box showing the index, type, name and label of all controls in a form. Wanted to add the routine they ran as well, and also got to thinking it would nice to be able to change it without having to tediously navigate the GUI as shown above.

Any pointers would be most appreciated. Thanks!

edit retag close merge delete

You need used getScriptEvents for get, and registerScriptEventfor set in form control, look: https://api.libreoffice.org/docs/idl/...

With Python it's possible simplify this.

( 2019-10-14 17:41:57 +0200 )edit

Sort by » oldest newest most voted

OK, thanks to Mauricio for pointing me in the right direction to get this figured out.

The confusing part was that the events assigned to a form control object are not (as one would presume) contained within the control itself, rather its parent, the form object.

Perhaps in the future the control object will have additional properties and methods for viewing, getting and setting events associated with it.

While Python is a very popular way to run macros in Libre Office, this discussion uses Basic. If Python's your bag, it shouldn't be that hard to refactor.

Assuming you have a form object "oForm" with 5 controls, and a push button control whose index in the list of form controls is "3" to which you wish to add or update its "mousePressed" event:

• oControl = oForm.getByIndex(3) gives you the button object.

• oControl.Name is the name of the button (useful for determining which control to work with)

• aControlEvents = oForm.getScriptEvents(3) will give you an array of all the events associated with the button, one element of which may or may not have an .EventMethod property set to "mousePressed"

So, like Mauricio said, getScriptEvents is how we can get a control's events. But what about setting?

It can vary depending on which type of control you're working with, as they support different events.

Here we'll again assume a push button, and use a couple of iterators. "i" is the index to the form's control, and "j" is the index to the array of the control's event(s).

• oControl = oForm.getByIndex(i) ' The control
• aControlEvents = oForm.getScriptEvents(i) ' The array of events associated with the control

An array entry in aControlEvents is a ScriptEventDescriptor type, containing five string entries:

• .ListenerType
• .EventMethod
• .ScriptType
• .ScriptCode

Create the descriptor: descriptor = New com.sun.star.script.ScriptEventDescriptor

To add an event, you call oForm.registerScriptEvent(i, descriptor)

So far as how to populate the descriptor fields:

For .EventMethod and .ListenerType, a pushbutton has 15 possible events. In design mode, examine a pushbutton's control properties, and look at the events tab. Match the label of the event you wish to affect to the .EventMethod shown below set and populate the two fields accordingly:

• .EventMethod -> .ListenerType
• mousePressed -> XMouseListener
• resetted -> XResetListener
• approveReset -> XResetListener
• approveAction -> XApproveActionListener
• mouseMoved -> XMouseMotionListener
• mouseDragged -> XMouseMotionListener
• mouseEntered -> XMouseListener
• mouseReleased -> XMouseListener
• keyReleased -> XKeyListener
• focusLost -> XFocusListener
• keyPressed -> XKeyListener
• mouseExited -> XMouseListener
• itemStateChanged -> XItemListener
• focusGained -> XFocusListener
• actionPerformed -> XActionListener

(Alternatively, examine xmloff/source/forms/formevents.cxx in the Libre source to see the available methods and listeners.)

Set .ScriptCode to the subroutine or method in your code you want to run, i.e., 'vnd.sun.star.script:Standard.Module1.YOUR_ROUTINE_NAME_HERE?language=Basic&location=document'

(If the routine is in a different module, or you've renamed "Module1", adjust as needed.)

.Script is just the string "Script", and .AddListenerParam is usually an empty string (unless you need ...

more

Hello,

First thank you for your research effort and post.

It appears, based upon the original question, you may be better off simply creating your own listeners (ie: events) with code rather than using setting/modifying in the properties. See:

CreateUnoListener Function [Runtime]

How to properly code a broadcaster and listener in LO Basic

and Pitonyak's book Open Office Macros Explained -> OOME PDF

( 2019-10-19 22:09:27 +0200 )edit

Yuck, thought that's what I was doing, but belatedly realized just running oForm.registerScriptEvent does nothing.

The part in https://www.openoffice.org/api/docs/common/ref/com/sun/star/script/XEventAttacherManager.html that was throwing me off was the section in the method's description, saying "If any object is attached under this index, then this event is attached automatically."

I took that to mean the result of oForm.getByIndex was the object so it would just take care of it, but looks like that's not the case.

Sure, the item shows up in getScriptEvents, but it's not attached to anything so doesn't fire when you click the button. Am trying to abstract this so it can work with any control and event, but starting with push buttons. Looks like there's a little more work to be done. Thanks for the comment!

( 2019-10-20 00:21:49 +0200 )edit

Argh, I am an idiot. In the event descriptor I had .ListenerType set to XActionListener instead of xMouseListener.

So, while oForm.registerScriptEvent did indeed produce an array entry that one could see with oForm.getScriptEvents, the listener type being incorrect prevented clicking the button from doing anything.

It's working now.

Thanks for all the comments. Take care!

( 2019-10-20 16:30:41 +0200 )edit

As a follow-up, it's easy to get and set a sheet or document's events, too, though the procedure is just a hair different.

You need:

A sheet (or document) object, oObj

A string, strEvent, representing the name of the event ("onFocus", "onDoubleClick", etc.)

A two-element variant array of type PropertyValue (com.sun.star.beans.PropertyValue), arrElements

Sheet and document objects have a getEvents method, in which there is a getByName method.

See oObj.getEvents.getElementNames for the element names.

See oObj.getEvents.getByName(strEvent) for any events assigned. It will either be empty, or have the two-element array.

To update it, use oObj.getEvents.replaceByName(strEvent, arrTabEvents)

Make element 1's ".Value" string empty to remove an event, or re-assign it to any other routine you want to use.

( 2019-10-22 04:57:09 +0200 )edit

Format for the arrElements array:

With arrElements(0)

.Name = "EventType"

.Handle = -1

.Value = "Script"

.State = 0


End With

With arrElements(1)

.Name = "Script"

.Handle = -1

.Value = "vnd.sun.star.script:Standard.Module1.<YOUR_ROUTINE_NAME_HERE>?language=Basic&location=document"

.State = 0


End With

( 2019-10-22 04:57:50 +0200 )edit
( 2019-10-22 06:10:46 +0200 )edit

Thanks, Ratslinger, I used getElementNames from the sheet / document's getElements method to acquire the event names.

Turns out that with a document object, the two arrTabEvent elements are reversed, so you'd put your routine in element zero, not one. Other than that, the procedure seems identical.

( 2019-10-22 06:23:28 +0200 )edit

Except for knowing how to do all this, and the one post (first link I posted) using it when adding a new control via code, haven't really figured out any use for the procedures.

Can't say there isn't any, just haven't come up a use for any of this. All seems simpler with just another line or two of code.

( 2019-10-22 06:30:35 +0200 )edit

For Python developers, I'm develop a library for LibreOffice that help us to simplify the API UNO. I added support for the question in this thread. Now, you can.

IMPORTANT:Not try this code only copy/paste, you need read the wiki first: https://gitlab.com/mauriciobaeza/zaz/...

import uno
import easymacro as app

def main(event=None):

doc = app.get_document()
sheet = doc.active
form = sheet.forms[0]

button = form['cmd_test']
# ~ Get events
events = button.events
for event in events:
app.debug(event)

# ~ Set events
# ~ Automatic call CONTROLNAME_EVENTNAME: cmd_test_action
macro = {'library': 'mymacros'}
button.remove_event()

# ~ if you want save changes
doc.save()
return

def cmd_test_action(event):
app.msgbox('Ok Action')
return


You can see how: https://gitlab.com/mauriciobaeza/zaz/...

more

Decided to try your library. Have stayed away from it for some time since it seems to lack documentation. Had multiple problems just getting the "Test" from here https://gitlab.com/mauriciobaeza/zaz/... to work. For example line 4 of Test script is missing :. There were other problems.

After all that, set a Calc file up with the button for script in your answer. Had a number of other problems. Finally got it all straightened out and the script worked.

Will attempt to duplicate some of the original problems such as one message received was something about 'This system is not supported'.

Initial reaction is that people with little Python exposure will have difficulty with this.

It does seem to have potential though.

( 2019-10-20 05:53:52 +0200 )edit

Thanks, this library it's continuous developer. Yes, the start is little hard, but more late, it's delicious work with this.

Please, it's better report any error in the tickets system the repository, if not is possible, a new thread in this site it's ok.

( 2019-10-20 06:08:24 +0200 )edit

No problem with your request for error reporting but some regard this post. For example, you should include:

import uno
import easymacro as app


in the script. Now I realized it was missing import's but had to figure out which ones. Also that to test as is it should be named as mymacros.py. Had to examine the code to figure this out.

Just simple items like that will make some people just give up saying "it doesn't work" and abandoning it.

( 2019-10-20 06:28:45 +0200 )edit