Macro to Permanently Remove a Toolbar Icon or the Toolbar Itself

I have a Writer Template that has a custom toolbar that I created. If a user chooses to NOT have support for some functionality in his odt file (Monthly Expenses) then the template removes that code module from the odt file it creates. But there are still one or two icons for Monthly Expenses that are left on the custom toolbar that I want to also remove permanently from the odt file’s custom toolbar so that the user cannot click on them. (They would point to macros in the module that was removed.) As another solution, I could create a second custom toolbar specific to the Monthly Expenses module and completely delete it, if that’s possible.

I did find some BASIC macro code that will allow you to remove an icon from a toolbar, or even remove the toolbar itself, but this removal is not permanent.

Sub RemoveTBarItem()

Dim oLayoutManager, Tbar, TBarSettings As Object

oLayoutManager = ThisComponent.CurrentController.Frame.LayoutManager
TBar = oLayoutManager.getElement(“private:resource/toolbar/custom_toolbar_c6e”)
TBarSettings = TBar.getSettings(true)
TBarSettings.removeByIndex(1)
TBar.setSettings(TBarSettings)
Tbar.updateSettings()

End Sub

In the above Sub, you can even use TBar .dispose() to get rid of the toolbar. But as I say, if you then save the document then reopen it, the toolbar is still there as well as the icon you tried to remove.

What I want is the same result that you can get using Tools → Customize → Toolbars where you select the document on the right side, at the top (Scope), then select your custom toolbar below that (Target), after which you can easily click on any of the icons on the toolbar then click the left pointing arrow to remove it – permanently. You can also click on the little gear-like icon beside the name of the icon and it will give you the option to permanently remove the custom toolbar altogether.

I need the same ind of functionality through a macro that is run in the template so that the unwanted icons, or custom toolbar, is not in the odt file. Any help would be greatly appreciated.

Thank you Gizmo. Your question itself answered what I was looking for and you opened a valuable discussion.

Hello,

Have code here to delete a custom toolbar. Have chosen a different route than your because this routine was also tested in Calc and will work with multiple custom toolbars present. Toolbar is named starting with custom_.

Option explicit

Sub DeleteCustomToolbar
    Dim sToolbarURL$ ' URL of the custom toolbar.
    Dim oModuleCfgMgr ' Module manager.
    Dim oUIElementsInfo As Variant
    Dim x as Integer
    Dim oPropertyValue
    Dim aPropertyValue
    Dim oValue as String
Rem CfgMgr for document
    oModuleCfgMgr = thisComponent.getUIConfigurationManager()
    oUIElementsInfo = oModuleCfgMgr.getUIElementsInfo(3)
    sToolbarURL = "none"
Rem search for your toolbar
    For x = LBound(oUIElementsInfo) to UBound(oUIElementsInfo)
       oPropertyValue = oUIElementsInfo(x)
       aPropertyValue = oPropertyValue(1)
      oValue = aPropertyValue.Value
Rem Name you gave custom toolbar
      If oValue = "custom_one" then
Rem if found get URL
          aPropertyValue = oPropertyValue(0)
          sToolbarURL = aPropertyValue.Value
          Exit for
      End if
    Next x
    if sToolbarURL = "none" then
Rem Named toolbar not found
       MsgBox " Toolbar not Present"
       Exit Sub
    End If
Rem check if selected toolbar has settings
Rem yes then delete & store result
    If (oModuleCfgMgr.hasSettings(sToolbarURL)) Then
        oModuleCfgMgr.removeSettings(sToolbarURL)
        oModuleCfgMgr.store()
    End If
End Sub

Just replace the custom_one name with yours - appears to be custom_toolbar.

2 Likes

Have further tested with other than custom_ and no problem. Not noted earlier but this is for deleting custom toolbars stored in the document.

Also, when deleting the toolbar, since it is a change to the document, the indicator to save will be on. To automatically save you can add the following lines to the end of the sub:

Dim odocument as object
Dim odispatcher as object
odocument = thisComponent.CurrentController.Frame
odispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
odispatcher.executeDispatch(odocument, ".uno:Save", "", 0, Array())

Edit:

One way to remove an item (after getting the wanted toolbar):

dim oSettings as object
oSettings = oModuleCfgMgr.getSettings(sToolbarURL, True)
oSettings.removeByIndex(2)
oModuleCfgMgr.replaceSettings(sToolbarURL, oSettings)

Remember to store & save the document.

1 Like

Hi Ratslinger. Thanks so much for your reply, it’s been really very helpful. I wold have replied sooner but I was playing with the code that you posted to see if I could remove any of the individual icons from the toolbar. I came up with the same code as you did below. However, I discovered that if you try to remove two icons in a row, say No 9 and 10, you really had to remove them in reverse order, 10 then 9, otherwise when the first was removed, the 10th icon then become the 9th.

It occurred to me that it would be useful to have a function that could be called with the URL of the toolbar and the name of an icon that would then return its location in the toolbar, starting at 0 from left to right. (After all, the toolbar may be modified by you of someone else.) So I came up with this:

Function getToolbarIconNo(ToolbarURL, IconUIName As String) As Integer

Dim oLayoutMgr, TBar, oContext As Object
Dim ChildCount, i As Integer
Dim ChildName As String

getToolbarIconNo = -1

oLayoutMgr = ThisComponent.CurrentController.Frame.LayoutManager
TBar = oLayoutMgr.getElement(ToolbarURL)
If isNull(TBar) Then Exit Function

oContext = TBar.getRealInterface().getAccessibleContext()
ChildCount = oContext.getAccessibleChildCount()
For i = 0 To ChildCount - 1
ChildName = oContext.getAccessibleChild(i).getAccessibleName()
If ChildName = IconUIName Then
getToolbarIconNo = i
Exit Function
End If
Next

End Function

You would use it this way:

If (oModuleCfgMgr.hasSettings(sToolbarURL)) Then

sToolbarURLSettings = oModuleCfgMgr.getSettings(sToolbarURL, True)

IconNo = getToolbarIconNo(sToolbarURL, "MonthlyExpenses")
If IconNo <> -1 Then
  sToolbarURLSettings.removeByIndex(IconNo)
  oModuleCfgMgr.replaceSettings(sToolbarURL, sToolbarURLSettings)

End If
IconNo = getToolbarIconNo(sToolbarURL, “SearchMontlyExpenses”)
If IconNo <> -1 Then
sToolbarURLSettings.removeByIndex(IconNo)
End If

oModuleCfgMgr.replaceSettings(sToolbarURL, sToolbarURLSettings)
oModuleCfgMgr.store()

End If

It seems like an awful lot of code to get the index of a named icon, but this was the best that I could do in an afternoon’s work. Note that, because I had to go through the Layout Manager, that I had to add the line:

      oModuleCfgMgr.replaceSettings(sToolbarURL, sToolbarURLSettings)

– after each icon removal, otherwise the toolbar settings would be out of sync and the function would return erroneous data.

Of course if you are certain of the index of the icons you wish to remove, and are sure no one will tamper with the the toolbar, you can simply ignore the function and use:

     sToolbarURLSettings.removeByIndex(10)
     sToolbarURLSettings.removeByIndex(9) ' remove this icon (Icons begin at 0 from left to right.)

– removing them in reverse order.

Something else that I found interesting – and frustrating – was that you could get the count of items on the toolbar by placing this line in your code:

    sToolbarURLSettings = oModuleCfgMgr.getSettings(sToolbarURL, True)
    iCount =  sToolbarURLSettings.getCount()

The above gives you the count of icons, including separators, on the toolbar.
Now, there is another Method that you can use with the Settings, and I tried to use it like this:

aChild = sToolbarURLSettings.getByIndex(0)

This line does not generate any kind of an error. And testing it with isNull() and isEmpty() both return false. But if you try to access aChild in any way, such as this:

MsgBox("aChild Properties " & Chr(10) & aChild.DBG_Properties)

You get this error message:

BASIC runtime error.
Object variable not set.

The problem appears to be that .getByIndex() always returns void for some reason. Too bad as it would make it so much easier to get the name of the icon (aChild) if only .geyByIndex() would work. Anyway, that’s as far as I’ve taken things.

Thanks again for all your great help, Ratslinger, I really appreciate it.

That is not how I do it in my menu creation. You need to remember that you are working with an index. If you remove one, the other following will be reduced in position. So in your example, with removing two consecutive, remove #9 twice.

Nope!

Quick test using MRI:

dim oSettings as object
  oSettings = oModuleCfgMgr.getSettings(sToolbarURL, True)
Mri oSettings
Dim oObj4
  oObj4 = oSettings.getByIndex(3)
  aPropertyValue = oObj4(2)
  oValue = aPropertyValue.Value
Print oValue

Result:
.


.
Appears you took the wrong approach with your code. It is fairly simple.

This is Excellent! So the code for looking for the name of the icon should be pretty straightforward. I wonder why, when you list the methods of Settings using .DBG_Methods, that it shows:

Void getByIndex(Long)

I really need to get that MRI tool. Anyway, thanks so much for all your great work on this! I really appreciate it.

need correct index:

hanya / MRI
.
[Tutorial] Introduction into object inspection with MRI
.

It’s not actually. Here is a routine I just tried:

Option explicit

Sub FindCustomItem
    Dim sToolbarURL$ ' URL of the custom toolbar.
    Dim oModuleCfgMgr ' Module manager.
    Dim oUIElementsInfo As Variant
    Dim x as Integer
    Dim y as Integer
    Dim z as Integer
    Dim oPropertyValue
    Dim aPropertyValue
    Dim oValue as String
    Dim oSettings as object
    Dim itemSettings
    Dim tbItemCount as integer
    oModuleCfgMgr = thisComponent.getUIConfigurationManager()
    oUIElementsInfo = oModuleCfgMgr.getUIElementsInfo(3)
    For x = LBound(oUIElementsInfo) to UBound(oUIElementsInfo)
        oPropertyValue = oUIElementsInfo(x)
        aPropertyValue = oPropertyValue(0)
        sToolbarURL = aPropertyValue.Value
        aPropertyValue = oPropertyValue(1)
        oValue = aPropertyValue.Value
        oSettings = oModuleCfgMgr.getSettings(sToolbarURL, True)
        tbItemCount = oSettings.Count
        For y = 0 to tbItemCount-1
            itemSettings = oSettings.getByIndex(y)
            For z = 0 to 4
                On Error Resume Next
                aPropertyValue = itemSettings(z)
                oValue = aPropertyValue.Name
                if oValue = "Label" then
                    oValue = aPropertyValue.Value
                    if oValue = "NAME_TO_FIND" then 
                         MsgBox "toolbar = " & "  " & sToolbarURL  & Chr(10) & "Item Index = " &  "  "  & y
                    End if
                End If
            Next z
        Next y
    Next x
End Sub

Note the On Error Resume Next.
This occurred on the Standard toolbar. As most settings have five items (CommandURL, Label, Type, Style and IsVisible), this has one with just Type. As I did not do a lot of testing, anyone using this should do further testing.

Also, the MsgBox displays the ToolbarURL and index. You can use these to delete an item.
.
Another item of interest is using a predefined item on your custom toolbar. These will be uno commands and the names do not appear in the Label. If used, just rename and then can be searched for. Otherwise the name is empty.

1 Like

Actually, when I said that it seemed like a lot of work, I was referring to my own Function that I wrote to get the name of the icon.That function –

Function getToolbarIconNo(ToolbarURL, IconUIName As String) As Integer

seemed like a lot of work when you consider that, in the code that you provided, you could already get the count of the icons, and the .getByIndex(n) was just sitting there, but I couldn’t get the stupid thing to work. (Turns out I was the stupid thing :wink: )

Yes, once you realize that getByIndex() is returning a set of Beans properties for the indexed item, you can nicely get all the information you need about any icon and decide whether or not you want to remove it, as you’ve nicely demonstrated with your new code example. Thanks!

Thanks also for the link to the MRI and the tutorial on how to use it. I’ll certainly take a look at that.

BTW the code you provided also turns out to be the solution to another problem I had posted about a little while ago:

How to Permanently Remove a Context Menu Item with a Macro (See below)

I slightly modified your code to look at Popup Menus instead of Toolbars as I suspected that’s where I’d find the Context Menu I was looking for:

Sub AccessUIConfigPopupMenus()

    Dim sCofigItemURL$ ' URL of the Configuration Item
    Dim oModuleCfgMgr ' Module manager.
    Dim oUIElementsInfo As Variant
    Dim x as Integer
    Dim oPropertyValue
    Dim aPropertyValue
    Dim oValue as String
REM CfgMgr for document
    oModuleCfgMgr = thisComponent.getUIConfigurationManager()
    oUIElementsInfo = oModuleCfgMgr.getUIElementsInfo(com.sun.star.ui.UIElementType.POPUPMENU)
    sCofigItemURL = ""
REM search for your Popuu menu
    For x = LBound(oUIElementsInfo) to UBound(oUIElementsInfo)
       oPropertyValue = oUIElementsInfo(x)
       aPropertyValue = oPropertyValue(0)
      oValue = aPropertyValue.Value
      
REM URL of the popup menu you're looking for
      If oValue = "private:resource/popupmenu/table" then
        sCofigItemURL = oValue
        Exit For
      End If
    Next x
      
    if sCofigItemURL = "" then
REM Popup menu not found
       MsgBox " Didn't find the popup menu for Tables"
       Exit Sub
    End If
REM check if selected Popup menu has settings
REM yes then delete & store result
    
    If (oModuleCfgMgr.hasSettings(sCofigItemURL)) Then
      
        URLSettings = oModuleCfgMgr.getSettings(sCofigItemURL, True)
        iCount = URLSettings.getCount
'        MsgBox("There are " & iCount & " popup itmes!") ' = 46

'        For i = 0 To iCount - 1
'          ItemBeans = URLSettings.getByIndex(i)
'          aPropertyValue = ItemBeans(0)
'          oName = aPropertyValue.Name
'          oValue = aPropertyValue.Value
'          MsgBox("Property Name = " & oName & " Property Value = " & oValue)
'        Next
            
        URLSettings.removeByIndex(45) ' remove this menu item
        URLSettings.removeByIndex(44) ' remove this menu item
        
        oModuleCfgMgr.replaceSettings(sCofigItemURL, URLSettings)
        ' In both cases you have to store() the chages you've made         
        oModuleCfgMgr.store()
    End If
End Sub

The For Loop I’ve commented out will give you a list of all the items in the menu:

Property Name = CommandURL Property Value = .uno:Cut
Property Name = CommandURL Property Value = .uno:Copy
Property Name = CommandURL Property Value = .uno:Paste
Property Name = CommandURL Property Value = .uno:PasteSpecialMenu
Property Name = Type Property Value = 1
Property Name = CommandURL Property Value = .uno:UpdateCurIndex
Property Name = CommandURL Property Value = .uno:EditCurIndex
Property Name = CommandURL Property Value = .uno:RemoveTableOf
Property Name = Type Property Value = 1
Property Name = CommandURL Property Value = .uno:UnsetCellsReadOnly
Property Name = CommandURL Property Value = .uno:MergeCells
Property Name = CommandURL Property Value = .uno:SplitCell
Property Name = CommandURL Property Value = .uno:TableInsertMenu
Property Name = CommandURL Property Value = .uno:TableDeleteMenu
Property Name = CommandURL Property Value = .uno:TableAutoFitMenu
Property Name = CommandURL Property Value = .uno:StyleMenu

etc.

You can also see this Popup (Context) Menu by going:

Tools → Customize → Context Menus, etc.

You can see that I’ve removed two of the items using remove ByIndex(). When I save the document after running the Sub, the removed items are permanently gone.

As it was your code example that provided the solution to this other problem, I think it only right that you should be the one to fill in this solution at that posting to get the full credit that you deserve for it. Thanks again for all your great help in these two nagging problems. I really appreciate it!

Here is the link to that posting:

1 Like

Thanks but not necessary. You actually provided the final code for that. You should answer/accept your own question there. Post again your code there and you can always link back.

Keep in mind, there is hardly anything new as far as code goes. Most of it is adopted from other code. Some of my code was from my working with menus which came from a variety of sources. My first few lines here are from Pitonyak and I have squeezed in a few lines of my own here and there.

Just glad you have something you can use.