A UNO listener is the paradigm used by OOo to inform external programs what it is doing. Consider a hospital worker as an analogy: Before I (the hospital worker) do anything interesting to treat patient Paolo, a 16-year-old boy from Argentina, I am required first to call his parents and ask permission. I don't have to call for everything, such as the most routine questions and physical examinations or some acute critical-care procedures. For some things, I must make the effort to speak to his parents, and for some things his parents may say "No, you cannot do it." In other circumstances, I may call only to provide status information. This is very similar to the UNO listeners. The code that you write is called the listener, and the thing to which you listen is called the broadcaster. In the previous example, the hospital worker is the broadcaster and Paolo's parents are the listeners.
If some portion of OOo supports listeners, you can listen to what it is doing. The routines required to listen to a specific OOo interface are specific to that interface. In other words, the set of subroutines and functions that can act as a listener to printing events is different from the set of subroutines and functions that can listen to keystroke events.
The first step in writing a listener is to determine what interface you must implement. The interface defines the subroutines and functions that you must write. Two different listener interfaces will have at least one required subroutine or function in common. To avoid the problems that this may cause, OOo Basic requires you to choose a string that prefixes each subroutine and function that you write for the listener.
All listeners are derived from the com.sun.star.lang.XEventListener. This means that all listeners must implement all of the routines required for the XEventListener. The XEventListener interface is simple because it defines only one subroutine: disposing(EventObject).
The disposing method is called when the broadcaster is about to become unavailable. In other words, the broadcaster will no longer exist and you should not use it. For example, a print job is finished so it is no longer required or a document is closing. To write the simplest listener possible, I will write an XEventListener. In Listing 15 I'll use the string prefix "first_listen_" for my first listener because it is descriptive.
Sub first_listen_disposing( vArgument ) MsgBox "Disposing In First Listener" End Sub
Listing 15 is all that is required for this simple listener. Use the CreateUNOListener to create a UNO listener that is associated with the macro in Listing 15. The first argument is the string prefix and the second argument is the name of the service to create.
The routine in Listing 16 creates the UNO listener and associates it with the routine in Listing 15. When the disposing method is called on vListener, it calls the routine in Listing 15.
Sub MyFirstListener Dim vListener Dim vEventObj As New com.sun.star.lang.EventObject Dim sPrefix$ Dim sService$ sPrefix = "first_listen_" sService = "com.sun.star.lang.XEventListener" vListener = CreateUnoListener(sPrefix, sService) vListener.disposing(vEventObj) End Sub
The two most difficult parts of creating a listener are determining which broadcaster and which listener interface to use. The rest of the steps are easier.
Determine the broadcaster to use.
Determine the listener interface to implement.
Create global variables for the listener and maybe the broadcaster.
Determine the prefix that you will use; make it descriptive.
Write all of the subroutines and functions that implement the listener.
Create the UNO listener and save it in the global variable.
Register the listener with the broadcaster.
When finished, remove the listener from the broadcaster.
Knowing which broadcaster to use is not always an easy task. It requires understanding how OOo is designed; this comes with experience. There is usually an obvious place to start looking-the current document, available from the variable ThisComponent, contains the document data. Start looking here for broadcasters that pertain to the document's data. For example, the document accepts an XPrintJobListener to monitor how printing is progressing. Most tasks related to user interactions-such as selecting text, moving the mouse, and entering keystrokes-all go through the document's controller.
Related to determining the broadcaster is choosing the correct listener to implement. If you think that you know which broadcaster to use, inspect the "add listener" methods on the broadcaster object. I was recently asked how to prevent a dialog from closing when the user clicks on the little X icon in the upper-right corner of a dialog. I reasoned that the dialog is probably the correct broadcaster to use, so I created a dialog and inspected the "dbg_methods" property to see which listeners it supports (each supported listener has "set" and "remove" methods). Another option is to determine the event type that is produced and then search to see which broadcasters support this event type. I usually do this by searching the OOo Developer's Guide and the Internet. (I'll discuss effective methods for finding information in Chapter 18, "Sources of Information.")
To listen for a selection change event, I need to implement an XSelectionChangeListener and register it with the current controller. This interface defines only one subroutine that must be implemented-selectionChanged (ObjectEvent). All events are derived from the XEventListener so the disposing(ObjectEvent) method must also be implemented. The fastest way to determe which subroutines and functions must be implemented is to create an instance of the listener by using the function CreateUNOListener and then inspect the dbg_methods property (see Listing 17 and Figure 5 ).
Sub InspectListenerMethods Dim sPrefix$ Dim sService$ Dim vService sPrefix = "sel_change_" sService = "com.sun.star.view.XSelectionChangeListener" vService = CreateUnoListener(sPrefix, sService) DisplayDbgInfoStr(vService.dbg_methods, ";", 35, "Methods") End Sub
The queryInterface method is from the interface com.sun.star.uno.XInterface, which is inherited from the XEventListener. You can ignore this method. However, you must implement the rest of the routines. Choose a descriptive prefix so that the code can contain more than one listener. See Listing 18 .
'All listeners must support this event Sub sel_change_disposing(vEvent) msgbox "disposing the selection change listener" End Sub Sub sel_change_selectionChanged(vEvent) Dim vCurrentSelection As Object vCurrentSelection = vEvent.source Print "Number of selected areas = " &_ vCurrentSelection.getSelection().getCount() End Sub
The routine in Listing 19 creates a listener by calling the function CreateUnoListener. When I tested this listener, I ran the macro and then the macro stopped running. The listener is still active and called by the broadcaster even though the macro is no longer running. In other words, the routine in Listing 19 registers a listener, and then it is finished. The broadcaster calls the listener when things occur. The listener must be stored in a global variable so that it's available later-for example, when it's time to remove the listener from the object or when one of the methods is called.
Global vSelChangeListener 'Must be global Global vSelChangeBroadCast 'Run this macro to start event intercepting Sub StartListeningToSelChangeEvents Dim sPrefix$ Dim sService$ sPrefix = "sel_change_" sService = "com.sun.star.view.XSelectionChangeListener" REM to register for a selection change, you must register with the REM current controller vSelChangeBroadCast = ThisComponent.getCurrentController 'Create a listener to intercept the selection change events vSelChangeListener = CreateUnoListener(sPrefix, sService) 'Register the listener to the document controller vSelChangeBroadCast.addSelectionChangeListener(vSelChangeListener) End Sub
The behavior of the code in Listing 19 is annoying. Every time a new selection is made, the code is called and it opens a dialog on the screen. This interferes with selecting text. To remove the listener, use the code in Listing 20 .
Sub StopListeningToSelChangeEvents ' removes the listener vSelChangeBroadCast.removeSelectionChangeListener(vSelChangeListener) End Sub
Tip | Store a copy of the listener in a global variable. If you use any other type, it won't work. |