How to trigger event with LibreOffice API to respond when closing a document?

Hello,

I open a LibreWriter document via the API with the desktop environment (com.sun.star.frame.Desktop) and then use LoadComponentFromURL, and can also control saving and closing the document very well.

However, I need an event in case the user closes the document manually. How can I retrieve an associated event and respond to it in my own application that previously opened the document?

[Tutorial] Introduction into object inspection with MRI
https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1document_1_1XDocumentEventBroadcaster.html
or maybe LibreOffice: XCloseBroadcaster Interface Reference

Thanks, I’ve done some searching for it in the meantime as well.

XDocumentEventBroadcaster contains the functions addDocumentEventListener, removeDocumentEventListener and notifyDocumentEvent. But I can’t find an example anywhere how to implement this?

Does anyone have a tip how I can implement this with the event of a closed document (e.g. in Writer)?

This depends on the language. Some languages allow to derive from classes, so you can create a subclass of corresponding com.sun.star.*.X*EventListener; some do not, and need some shim, as with Basic’s CreateUnoListener. Both approaches are shown at Creating Event Listeners.

The attached module allows you to track all events of all open documents that are displayed in a new Calc document. May be useful for educational purposes.
To start, use the StartDocumentListener macro.

Option Explicit
Option Compatible

Global oBroadcaster As Object
Global oListener As Object
Global oSheet As Object
Global row As Long

Sub StartDocumentListener
  If oSheet Is Nothing Then
    oSheet = StarDesktop.LoadComponentFromUrl("private:factory/scalc", "_blank", 0, Array()).Sheets(0)
    oSheet.getCellRangeByPosition(0, row, 2, row).setDataArray Array(Array("Time", "Document", "Event"))
  End If

  If oListener Is Nothing Then   
    oListener=CreateUnoListener("DocList_", "com.sun.star.document.XDocumentEventListener")
    oBroadCaster=CreateUnoService("com.sun.star.frame.GlobalEventBroadcaster")
    oBroadCaster.addDocumentEventListener(oListener)
  End If  
End Sub

Sub StopDocumentListener
  If Not (oListener Is Nothing) Then   
    oBroadCaster.removeDocumentEventListener(oListener)
    oListener=Nothing
    oBroadcaster=Nothing  
  End If  
End Sub

Sub DocList_documentEventOccured(oEvent)
  On Error GoTo Errlabel
  Dim arr
  arr=Array(Empty, oEvent.Source.Title, oEvent.EventName)
  arr(0)=Format(Now(), "YYYY-MM-DD HH-MM-SS")
  row=row+1
  oSheet.getCellRangeByPosition(0, row, 2, row).setDataArray Array(arr)
ErrLabel:  
End Sub

Sub DocList_disposing(oEvent)
End Sub

Thanks for your answer and the example.

My approach so far is a bit simpler and I wonder if the only way for the event really needs to be implemented with CreateUnoService:

vServiceManager = CreateOleObject(‘com.sun.star.ServiceManager’);

vDesktop = vServiceManager.createInstance(‘com.sun.star.frame.Desktop’);

vDocument = vDesktop.LoadComponentFromURL(‘private:factory/swriter’, ‘_blank’, 1, VarArrayOf([{Hidden}]));

Thanks for the help…

You have not described why the answers above do not fit your case. Given that you use a Pascal, the event listeners may be used fully, including e.g. throwing
CloseVetoException
from XCloseListener::queryClosing.

However, there may be another way to bind something to the document events - but note that that is a document change, so it would be saved with the document, and make the saved handle to attempt to execute on following document operations (with respective security warnings). For that, you assign handlers (macros) to the events by name, like this (Basic code):

Sub SetupOnPrepareUnload(oDoc, sMacro)
	Dim descriptor(1) as new com.sun.star.beans.PropertyValue
	descriptor(0).Name = "EventType"
	descriptor(0).Value = "Script"
	descriptor(1).Name = "Script"
	descriptor(1).Value = "vnd.sun.star.script:" & sMacro & "?language=Basic&location=application"
	
	oDoc.getEvents().replaceByName("OnPrepareUnload", descriptor())
End Sub	

This has only one advantage IMO: the possibility to stop unloading documents from languages that do not allow throwing exceptions, like Basic.