Toolbar with functional ComboBox, DropdownBox etc

I try to create the ComboBox in the toolbar with functional mouse selecting and keyboard selecting. I found 2 examples.

  1. The extension Complex Toolbar - but there isn’t the possibility how to detect the selected item.

  2. Somewhere from OOo forum I reconstructed the example with the BeanShell. It is partially functional - OK for mouse selecting in the ComboBox, but no for Enter. And the Libre will crash with the closing the sheet.
    ComplexToolbar-combobox-BeanShell.ods (20.7 kB)

In second example, I’m not able to rewrite the BeanShell script to the Python. But I think the better usage is the ProtocolHandler in the first example, but I’m not able to create the needy listeners for the toolbar.

The idea is to have the ComboBox for the Font selection, but only with the elected fonts. I don’t want to have in the font list the fonts with the restrictions like Windows C-fonts etc. So I try to create the customable ComboBox only with the friendly fonts :slight_smile: (like the fonts with the OFL license, for example Liberation Serif)

To get the list with the fonts is easy.

Sub fontsInfo
	dim oDoc as object, oWindow as object, oFonts()
	xray oFonts
End Sub

What version of LibreOffice you use?


on the second point: The standard awt combobox window has no mechanism for detecting Enter or other keystrokes. You have to write that yourself. I was too lazy to do that. See the below snippet as a hint where to go …

Good luck,


Apache Office 4.1.6. on Win10x64

function generateCBOXWindow(wParent as as
'the itemlistener, which is added to the toolbar combobox
listItemListener = createUnoListener("CBOX_ITEM_", "")
textListener = createUnoListener("CBOX_TEXT_", "")
keyListener = createUnoListener("CBOX_KEY_", "")
oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "combobox", or
'xray oWindow

oWindow.setText("Enter Command Here")
oWindow.addItems(Array( "test", "foo", "bar", "test2", "foo2", "bar2" ), 0)
generateCBOXWindow = oWindow
end function

' the to get the combobox events back to OO Basic
sub CBOX_ITEM_itemStateChanged(event as
lSelected = event.selected
s = "selection change to item # " + lSelected
if lSelected >=0 then s = s & ": " & event.source.items(lSelected)
end sub

' the to get the Text events back to OO Basic
sub CBOX_TEXT_textChanged(event as
s = "text event" 
end sub

' the to get the Text events back to OO Basic
sub CBOX_KEY_keyPressed(event as
s = "key pressed, code " + event.KeyCode 
if event.KeyCode = then
	xray event
'here, you have to manually match the text entered with the options and update the selcted item, if appropriate
end if
end sub

' the to get the Text events back to OO Basic
sub CBOX_KEY_keyReleased(event as
s = "key released, code " + event.KeyCode 
'xray event
end sub

Thanks a lot :-). It is functional and I understand how it works in Basic.

The remaining problem for me is to convert BeanShell to Python :slight_smile:

… for Python have look at Apache OpenOffice Community Forum - Write own Toolbars in Python - (View topic).
Good luck,

I ran function doItAll and it shows errors with constants in LibreOffice ( Win10x64)

so I substituted ones to normal numbers

line 66: uno.getConstantByName("") -> 3
line 78: uno.getConstantByName("") -> 0
line 172: uno.getConstantByName('') -> 0

But I don’t know how to repair next error :-(.

The code with repaired constants

import uno
import unohelper
from subprocess import Popen

from import XUpdatable # type:ignore

from import XToolbarController # type:ignore
from import XStatusListener # type:ignore

from import XInitialization # type:ignore

from import Point # type:ignore
from import XItemListener # type:ignore
from import XAdjustmentListener # type:ignore

from import PropertyValue # type:ignore

# define ctx, doc, smgr for (i) invocation from VS Code or (ii) invocation from OO execute macro
if __name__ == '__main__': 
    # invocation from VS Code
    localContext = uno.getComponentContext()
    resolver = localContext.ServiceManager.createInstanceWithContext("", localContext )
    # start OO
    lo_proc = Popen('"C:\Program Files (x86)\OpenOffice 4\program\soffice.exe" -accept=socket,host=localhost,port=2002;urp;', shell=True)
    ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )

    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext( "",ctx)
    doc = desktop.loadComponentFromURL('private:factory/scalc', '_default', 0, ())

elif __name__ == 'ooo_script_framework':
    # invocation from OO execute macro
    doc = XSCRIPTCONTEXT.getDocument() # type: ignore
    ctx = XSCRIPTCONTEXT.getComponentContext() # type: ignore
    smgr = ctx.ServiceManager
    raise Exception('__name__ is unknown: ' + __name__) 

def xray(target):
    global ctx
    mspf = ctx.ServiceManager.createInstanceWithContext("", ctx)
    script_provider = mspf.createScriptProvider("")
    script = script_provider.getScript("")
    script.invoke((target,), (), ())

def logMessage(mes):
    global logSheet
    global logRow
    logSheet.getCellByPosition(0,logRow).String = mes
    logRow += 1

# wParent:
# x, y, width, height: int
# wServiceName: str
# wAttribute: long
# returns ->
def makeAwtWindow(wParent, x, y, width, height, wServiceName, wAttribute):
    global smgr
    global ctx
    oToolkit = smgr.createInstanceWithContext("", ctx)

    # create WindowDescriptor
    wd = uno.createUnoStruct("")
    wd.Type = 3 #uno.getConstantByName("")
    wd.Parent = wParent
    wd.Bounds = uno.createUnoStruct("", x, y, width, height)
    wd.ParentIndex = -1
    wd.WindowAttributes = wAttribute
    wd.WindowServiceName = wServiceName

    return oToolkit.createWindow(wd)

def TestAwt():
    global doc
    parentWindow = doc.CurrentController.Frame.ContainerWindow
    wAttribute = uno.getConstantByName("") | 0 #uno.getConstantByName("")
    oWindow = makeAwtWindow(parentWindow, 100, 100, 300, 200, "combobox", wAttribute)

class ToolbarItem(unohelper.Base, XInitialization, XUpdatable, XToolbarController, XStatusListener):
    def __init__( self, ctx, propModuleIdentifier, propFrame, propServiceManager, propParentWindow, propIdentifier, propCommandUrl ):
        self.sCommandUrl = propCommandUrl.Value
        print('in ToolbarItem constructor: ' + self.sCommandUrl)
    def initialize(self, args):
        logMessage("initialize " + self.sCommandUrl)
    def update(self):
        logMessage("update " + self.sCommandUrl)
    def statusChanged(self, state):
        logMessage("statusChanged " + self.sCommandUrl)

    def execute(self, KeyModifier):
        logMessage("execute " + self.sCommandUrl)

    def click(self):
        logMessage("click " + self.sCommandUrl)

    def doubleClick(self):
        logMessage("doubleclick " + self.sCommandUrl)

    def createPopupWindow(self): 
        logMessage("createPopupWindow " + self.sCommandUrl)
        return None

    def createItemWindow(self, wParent):

class ToolbarItemComboBox(ToolbarItem, XItemListener):
    asOptions = ()
    sText = ""

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("") | uno.getConstantByName("")

        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "combobox", wAttribute)
        oWindow.addItems(self.asOptions, 0)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def itemStateChanged(self, event):
        self.lSelected = event.Selected
        s = "selection change to item # " + str(self.lSelected)
        if self.lSelected >= 0:
            s = s + ": " + event.Source.getItem(self.lSelected)
        s = s + ', ' + self.sCommandUrl

class ToolbarItemComboBox2(ToolbarItemComboBox):

class ToolbarItemSchrollbar(ToolbarItem, XAdjustmentListener):

    def createItemWindow(self, wParent):
        logMessage("createItemWindow start " + self.sCommandUrl)
        wAttribute = uno.getConstantByName("")
        oWindow = makeAwtWindow(wParent, 0, 0, 230, 20, "scrollbar", wAttribute)
        oWindow.setValues(50, 10, 100)
        logMessage("createItemWindow end " + self.sCommandUrl)
        return oWindow

    def adjustmentValueChanged(self, event):
        s = "schrollbar changed to " + str(event.Value) + ', ' + self.sCommandUrl

def createToolbar(toolbarResourceUrl, toolbarUIName):

# delete toolbar if existing
    oLM = doc.CurrentController.Frame.LayoutManager
    if oLM.getElement(toolbarResourceUrl) is not None:
# create toolbar
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)        
    oToolbarSettings.UIName = toolbarUIName 
# dock the toolbar in the docking area. Not sure if necessary.
    oLM.dockWindow(toolbarResourceUrl, 0, Point(0, 0)) #oLM.dockWindow(toolbarResourceUrl, uno.getConstantByName(''), Point(0, 0)) 

def insertToolbarItems(toolbarResourceUrl, aItem):
# register the itemService at the Toolbarfactory
    oo = smgr.createInstanceWithContext("", ctx)
    oLM = doc.CurrentController.Frame.LayoutManager
    oEL = oLM.getElement(toolbarResourceUrl)
    oToolbarSettings = oEL.getSettings(True)

    for Item in aItem:
        if oo.hasController( Item.itemCommandUrl, ""):
            oo.deregisterController( Item.itemCommandUrl, "")
        oo.registerController( Item.itemCommandUrl, "", Item.itemService)

        prop1 = PropertyValue()
        prop1.Name = "CommandURL" 
        prop1.Value = Item.itemCommandUrl
        prop2 = PropertyValue()
        prop2.Name = "Label" 
        prop2.Value = Item.itemLabel

        uno.invoke( oToolbarSettings, "insertByIndex", (oToolbarSettings.Count, uno.Any("[]", (prop1, prop2)))) 


def doItAll():
    global logSheet
    global logRow


    #for the logging ...
    logRow = 0
    logSheet = doc.Sheets.getByIndex(0)



    xSM = ctx.getValueByName("/singletons/")

    toolbarResourceUrl = "private:resource/toolbar/custom_ms777"
    toolbarUIName = "toolbarUIName ms777"

    ToolbarItemComboBox.asOptions = ( "test", "foo", "bar", "test2", "foo2", "bar2" )
    ToolbarItemComboBox.sText = "Combo Box 1"
    ToolbarItemComboBox.itemLabel = "LabelCombobox 1"
    ToolbarItemComboBox.itemCommandUrl = ".uno:ms777Combobox"
    ToolbarItemComboBox.itemService = "ms777.ToolbarCombobox"
    ToolbarItemComboBox.itemServiceImpl = "ms777.impl.ToolbarCombobox"

    ToolbarItemComboBox2.asOptions = ( "option A", "Karl Heinz")
    ToolbarItemComboBox2.sText = "Combo Box 2"
    ToolbarItemComboBox2.itemLabel = "LabelCombobox 2"
    ToolbarItemComboBox2.itemCommandUrl = ".uno:ms777Combobox2"
    ToolbarItemComboBox2.itemService = "ms777.ToolbarCombobox2"
    ToolbarItemComboBox2.itemServiceImpl = "ms777.impl.ToolbarCombobox2"

    ToolbarItemSchrollbar.itemLabel = "Label Scrollbar"
    ToolbarItemSchrollbar.itemCommandUrl = ".uno:ms777Scrollbar"
    ToolbarItemSchrollbar.itemService = "ms777.ToolbarScrollbar"
    ToolbarItemSchrollbar.itemServiceImpl = "ms777.impl.ToolbarScrollbar"

#    prop1 = PropertyValue()
#    prop1.Name = "CommandURL" 
#    prop1.Value = itemComboBoxCommandUrl 
#    xt = ssf.createInstanceWithArgumentsAndContext((1, 2, 3, 4, 5, prop1), ctx)
#    xray(xt)

#    xray(xSM)

    createToolbar(toolbarResourceUrl, toolbarUIName)

    for Item in (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar):
        ssf  = unohelper.createSingleServiceFactory( Item,  Item.itemServiceImpl,  (Item.itemService,) )

    insertToolbarItems(toolbarResourceUrl, (ToolbarItemComboBox, ToolbarItemComboBox2, ToolbarItemSchrollbar))

if __name__ == '__main__':
    lo_proc.wait() # when run from VS code, better wait until AO is finished. Otherwise, some calls goto nowhere ...

You forgot to declare c.s.s.awt.WindowClass.TOP

Please clean up the mess!

… everything was done on AO 4.1.6, see the original post. I just installed LO and made a LO version Apache OpenOffice Community Forum - Write own Toolbars in Python - (View topic)

One small progress for point 1.
I emailed with Gérard Deneux, the author of extension CADLO. This extension also use Complex Toolbar and there is for example ToggleDropDown Button. And Gérard wrote me there is the class XListenerHelper for handling it. But I’m not able to simplify it and add to the Complex Toolbar Extension, still too much complicated for me :-(.

The links with help from Gérard (he hadn’t time to help me more)

not really sure what kind of control you want to insert into the toolbar.

Did you test the latest version of Apache OpenOffice Community Forum - Write own Toolbars in Python - (View topic) ?

In principle, it is possible to insert each and every UnoControl into the toolbar, even a complete UnoDialog.
Please check the above link and let me know what is missing

Good luck, ms777

Edit: this is a minimum example on LO (x64)
toolbar.ods (10.9 KB)

1 Like

Hello, I tested it, the control of toolbar is OK, but when I close the sheet and open new one, then new sheet isn’t visible :-(. Video with example (!rename odt to mp4!)
Record_2022_12_04_16_57_03_719.mp4.RENAMED.ODT (976.0 kB)
LibreOffice (Win10x64)

And after reopen toolbar.ods it shows the error

The solution for point 1. Tested on Win10x64 Libre
Rename odt to oxt and install it as Extension, it will add the Toolbar to Writer.
ComplexToolbar.oxt.RENAMED.ODT (5.3 kB)

It shows msgbox if some text is written in Editfield and pressed Enter. Or it changes the font for selected text if some item is selected in Combobox.

There is def dispatch in class SampleDispatch in file for the “listeners” for keyboard and mouse :-).