I tried it in Python and new problem is if user change the Module. Key-press selects the current line, but if user will switch to other Module (in same or other library), Libre changes the frame with Basic code to some “new” frame and I didn’t discover how to add new listener to “new” frame.
I run py code from external editor. Run LibreOffice with socket for Python, run Basic editor, and then this code. It will color the background of Basic IDE and key-press will select the current line. If you change the Module (for example in Object Catalog), then listeners from “old” frame are removed (event WindowHidden in ListenerWindow), but I don’t know how to detect “new” frame with Basic code from “new” Module.
import uno, unohelper
from com.sun.star.awt import XWindowListener
from com.sun.star.awt import WindowEvent
from com.sun.star.awt import XKeyListener
from com.sun.star.awt import KeyEvent
from com.sun.star.lang import EventObject
IDE=None # frame with Basic Code in Basic Editor
IDEb=False # for faster recursion in findIDE()
IDE1=None # object of IDE with writable properties
KEYlistener=None # listener for IDE
IDEparent=None # parent of IDE
WINDOWlistener=None # listener for IDEparent
def connectLibre():
"""start connection with LibreOffice"""
try:
localContext=uno.getComponentContext()
resolver=localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
ctx=resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
except:
ctx=None
if ctx==None:
print("!!! Failed to connect LibreOffice !!!")
import sys
sys.exit()
else:
return ctx
def mri(obj):
"""MRI"""
m=createUnoService("mytools.Mri")
m.inspect(obj)
def createUnoService(sName):
"""Basic: CreateUnoService()"""
localContext=uno.getComponentContext()
resolver=localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
ctx=resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
return ctx.ServiceManager.createInstanceWithContext(sName, ctx)
def hasUnoInterfaces(obj, *interfaces):
"""Basic: HasUnoInterfaces()"""
return set(interfaces).issubset(t.typeName for t in obj.Types)
def rgb(r, g, b):
"""Basic: RGB()"""
return 256*256*r+256*g+b
def findIDE(obj):
"""find frame with Basic Code"""
global IDE, IDEb
n=obj.AccessibleChildCount
if IDEb: # IDE is already found
return
if obj.AccessibleRole==61: # frame with Basic Code found
IDE=obj
IDEb=True
return
if n>0: # has child
i=0
while i<n: # test children
if IDEb: # IDE is already found
return
o=obj.getAccessibleChild(i)
if not isinstance(o, type(None)):
findIDE(o.AccessibleContext)
i+=1
def setBackground(color):
"""set background color of frame with Basic Code"""
global IDE1
IDE1.Background=color
def IDEjob():
"""set keyboard listener to frame with Basic Code"""
global IDE, IDEb, IDE1, IDEparent, WINDOWlistener, KEYlistener
ctx=connectLibre()
StarDesktop=ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
for oComp in StarDesktop.Components:
if hasUnoInterfaces(oComp, "com.sun.star.lang.XServiceInfo"):
if oComp.supportsService("com.sun.star.script.BasicIDE"):
findIDE(oComp.CurrentController.ComponentWindow.AccessibleContext) # set global variable IDE, that is frame with Basic Code
if not isinstance(IDE, type(None)): # Basic Editor is open
# set key listener
IDE1=IDE.AccessibleParent.AccessibleContext.getAccessibleChild(IDE.AccessibleIndexInParent)
KEYlistener=ListenerKey()
IDE1.addKeyListener(KEYlistener)
# set window listener
IDEparent=IDE.AccessibleParent
WINDOWlistener=ListenerWindow()
IDEparent.addWindowListener(WINDOWlistener)
setBackground(rgb(255, 255, 235)) # change background that means keyboard listener is active
class ListenerWindow(unohelper.Base, XWindowListener):
"""window listener"""
def __init__(self):
pass
def windowResized(self, evt: WindowEvent):
pass
def windowMoved(self, evt: WindowEvent):
pass
def windowShown(self, evt: EventObject):
setBackground(rgb(255, 215, 215)) # change background of IDE if WINDOWlistener isn't removed
return
def windowHidden(self, evt: EventObject):
global IDE1, IDEparent, WINDOWlistener, KEYlistener
# remove listeners
IDE1.removeKeyListener(KEYlistener)
IDEparent.removeWindowListener(WINDOWlistener)
setBackground(rgb(255, 255, 255))
return
def disposing(self, evt: EventObject):
pass
class ListenerKey(unohelper.Base, XKeyListener):
"""keyboard listener"""
def __init__(self):
pass
def keyPressed(self, evt: KeyEvent):
pass
def keyReleased(self, evt: KeyEvent):
o=evt.Source.AccessibleContext
iLines=o.AccessibleChildCount # count of lines with Basic code
i=0
while i<iLines:
oLine=o.getAccessibleChild(i)
if oLine.CaretPosition!=-1: # line with cursor
oLine.setSelection(0, oLine.CaretPosition) # select current line
break
i+=1
return
def disposing(self, evt: EventObject):
pass
IDEjob()
I don’t know how to end the communication with LibreOffice and Python editor, when I close LibreOffice, it stays run but it is invisible and I must end it via Ctrl+Alt+Del.
Tested in Libre 7.6.1.2 Win10