1
0
Fork 0
libreoffice/wizards/source/sfwidgets/SF_ContextMenu.xba
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

574 lines
No EOL
27 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_ContextMenu" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
REM === The SFWidgets library is one of the associated libraries. ===
REM === Full documentation is available on https://help.libreoffice.org/ ===
REM =======================================================================================================================
Option Compatible
Option ClassModule
Option Explicit
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
&apos;&apos;&apos; SF_ContextMenu
&apos;&apos;&apos; ==============
&apos;&apos;&apos; Complete a predefined context menu with new items.
&apos;&apos;&apos;
&apos;&apos;&apos; A context menu is obtained by a right-click on several areas of a document.
&apos;&apos;&apos; Each area determines its own context menu.
&apos;&apos;&apos; (Consider right-clicking on a cell or on a sheet tab in a Calc document).
&apos;&apos;&apos; Each component model has its own set of context menus.
&apos;&apos;&apos;
&apos;&apos;&apos; A context menu is usually predefined at LibreOffice installation.
&apos;&apos;&apos; Customization is done statically with the Tools + Customize dialog.
&apos;&apos;&apos; The actual service provides a mean to make temporary additions at
&apos;&apos;&apos; the bottom of a context menu. Those changes are lost when the document is closed.
&apos;&apos;&apos;
&apos;&apos;&apos; The name of a context menu is the last component of the resource URL:
&apos;&apos;&apos; &quot;private:resource/popupmenu/the-name-here&quot;
&apos;&apos;&apos;
&apos;&apos;&apos; Context menu items are either:
&apos;&apos;&apos; - usual items
&apos;&apos;&apos; - line separators
&apos;&apos;&apos; Checkboxes or radio buttons are not supported.
&apos;&apos;&apos;
&apos;&apos;&apos; A context menu is setup in next sequence:
&apos;&apos;&apos; 1. Define each menu item, submenu or line separator with AddItem(...)
&apos;&apos;&apos; 2. Activate() the menu to make it visible and active
&apos;&apos;&apos; 3. If meaningful, it can be temporarily deactivate with Activate(False)
&apos;&apos;&apos; 4. Items can be optionally added, and the menu might be made active again
&apos;&apos;&apos; 5. Dispose() cleans the memory resources
&apos;&apos;&apos;
&apos;&apos;&apos; Definitions:
&apos;&apos;&apos; SubmenuCharacter: the character or the character string that identifies how menus are cascading
&apos;&apos;&apos; Default = &quot;&gt;&quot;
&apos;&apos;&apos; Can be set when invoking the ContextMenu service
&apos;&apos;&apos; ShortcutCharacter: the underline access key character
&apos;&apos;&apos; Default = &quot;~&quot;
&apos;&apos;&apos;
&apos;&apos;&apos; Service invocation:
&apos;&apos;&apos; From a document, calc, writer, formdocument or datasheet service:
&apos;&apos;&apos; Dim MenusList As Variant, oCtxMenu As Object
&apos;&apos;&apos; MenusList = doc.ContextMenus()
&apos;&apos;&apos; &apos; Returns a list of available context menus as strings
&apos;&apos;&apos; Set oCtxMenu = doc.ContextMenus(ContextMenuName, SubmenuChar = &quot;&gt;&gt;&quot;)
&apos;&apos;&apos; &apos; Returns a context menu service instance
&apos;&apos;&apos;
&apos;&apos;&apos; Menus and submenus
&apos;&apos;&apos; To create a context menu with submenus, use the character defined in the
&apos;&apos;&apos; SubmenuCharacter property while creating the menu entry to define where it will be
&apos;&apos;&apos; placed. For instance, consider the following menu/submenu hierarchy.
&apos;&apos;&apos; Item A
&apos;&apos;&apos; Item B &gt; Item B.1
&apos;&apos;&apos; Item B.2
&apos;&apos;&apos; ------ (line separator)
&apos;&apos;&apos; Item C &gt; Item C.1 &gt; Item C.1.1
&apos;&apos;&apos; Item C.1.2
&apos;&apos;&apos; Item C &gt; ------ (line separator)
&apos;&apos;&apos; Item C &gt; Item C.2 &gt; Item C.2.1
&apos;&apos;&apos; Item C.2.2
&apos;&apos;&apos; Next code will create the menu/submenu hierarchy
&apos;&apos;&apos; With myMenu
&apos;&apos;&apos; .AddItem(&quot;Item A&quot;, Command := ...)
&apos;&apos;&apos; .AddItem(&quot;Item B&gt;Item B.1&quot;, Script := ...)
&apos;&apos;&apos; .AddItem(&quot;Item B&gt;Item B.2&quot;, ...)
&apos;&apos;&apos; .AddItem(&quot;---&quot;)
&apos;&apos;&apos; .AddItem(&quot;Item C&gt;Item C.1&gt;Item C.1.1&quot;, ...)
&apos;&apos;&apos; .AddItem(&quot;Item C&gt;Item C.1&gt;Item C.1.2&quot;, ...)
&apos;&apos;&apos; .AddItem(&quot;Item C&gt;---&quot;)
&apos;&apos;&apos; .AddItem(&quot;Item C&gt;Item C.2&gt;Item C.2.1&quot;, ...)
&apos;&apos;&apos; .AddItem(&quot;Item C&gt;Item C.2&gt;Item C.2.2&quot;, ...)
&apos;&apos;&apos; End With
&apos;&apos;&apos;
&apos;&apos;&apos; Example: Add 2 items to the general context menu of a Writer document
&apos;&apos;&apos; Sub SetContextMenu(oWriter As Object)
&apos;&apos;&apos; Dim oMenu As Object
&apos;&apos;&apos; Set oMenu = oWriter.ContextMenus(&quot;text&quot;)
&apos;&apos;&apos; With oMenu
&apos;&apos;&apos; .AddItem(&quot;About&quot;, Command := &quot;.uno:About&quot;)
&apos;&apos;&apos; .AddItem(&quot;Run&quot;, Script := &quot;vnd.sun.star.script:Standard.Module1.MyFunction?language=Basic&amp;location=document&quot;)
&apos;&apos;&apos; &apos; MyFunction is a Sub without argument
&apos;&apos;&apos; .Activate()
&apos;&apos;&apos; .Dispose()
&apos;&apos;&apos; End With
&apos;&apos;&apos; End Sub
&apos;&apos;&apos;
&apos;&apos;&apos;
&apos;&apos;&apos; Detailed user documentation:
&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_contextmenu.html?DbPAR=BASIC
&apos;&apos;&apos;
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
REM ================================================================== EXCEPTIONS
REM ============================================================= PRIVATE MEMBERS
Private [Me] As Object
Private ObjectType As String &apos; Must be ContextMenu
Private ServiceName As String
&apos; Menu descriptors
Private Component As Object &apos; com.sun.star.lang.XComponent
Private ResourceURL As String &apos; private:resource/popupmenu/...
Private ConfigManager As Object &apos; com.sun.star.ui.XUIConfigurationManager
Private MenuTree As Variant &apos; Dictionary nodename - com.sun.star.ui.ItemDescriptor pair
Private SubmenuChar As String &apos; Delimiter in menu trees
Private MenuContainer As Object &apos; com.sun.star.container.XIndexAccess
REM ============================================================ MODULE CONSTANTS
Private Const _UnderlineAccessKeyChar = &quot;~&quot;
Private Const _DefaultSubmenuChar = &quot;&gt;&quot;
Private Const _SeparatorChar = &quot;---&quot;
Private Const cstUnoPrefix = &quot;.uno:&quot;
REM ====================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
Set [Me] = Nothing
ObjectType = &quot;ContextMenu&quot;
ServiceName = &quot;SFWidgets.ContextMenu&quot;
Set Component = Nothing
Set ConfigManager = Nothing
ResourceURL = &quot;&quot;
Set MenuTree = Nothing
SubmenuChar = _DefaultSubmenuChar
Set MenuContainer = Nothing
End Sub &apos; SFWidgets.SF_ContextMenu Constructor
REM -----------------------------------------------------------------------------
Private Sub Class_Terminate()
Call Class_Initialize()
End Sub &apos; SFWidgets.SF_ContextMenu Destructor
REM -----------------------------------------------------------------------------
Public Function Dispose() As Variant
If Not IsNull(MenuTree) Then Set MenuTree = MenuTree.Dispose()
Call Class_Terminate()
Set Dispose = Nothing
End Function &apos; SFWidgets.SF_ContextMenu Explicit Destructor
REM ================================================================== PROPERTIES
REM -----------------------------------------------------------------------------
Property Get ShortcutCharacter() As Variant
&apos;&apos;&apos; The ShortcutCharacter property specifies character preceding the underline access key
ShortcutCharacter = _PropertyGet(&quot;ShortcutCharacter&quot;)
End Property &apos; SFWidgets.SF_ContextMenu.ShortcutCharacter (get)
REM -----------------------------------------------------------------------------
Property Get SubmenuCharacter() As Variant
&apos;&apos;&apos; The SubmenuCharacter property specifies the character string indicating
&apos;&apos;&apos; a sub-menu in a popup menu item
SubmenuCharacter = _PropertyGet(&quot;SubmenuCharacter&quot;)
End Property &apos; SFWidgets.SF_ContextMenu.SubmenuCharacter (get)
REM ===================================================================== METHODS
REM -----------------------------------------------------------------------------
Public Sub Activate(Optional ByVal Enable As Variant) As Variant
&apos;&apos;&apos; Make the added items of the context menu available for execution,
&apos;&apos;&apos; or, at the opposite, disable them, depending on the argument.
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Enable: When True (default), the new items of the context menu are made visible.
&apos;&apos;&apos; When False, they are suppressed.
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; None
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; Sub SetContextMenu(oWriter As Object) &apos; oWriter is a SFDocuments.SF_Writer service instance
&apos;&apos;&apos; Dim oMenu As Object
&apos;&apos;&apos; Set oMenu = oWriter.ContextMenus(&quot;text&quot;)
&apos;&apos;&apos; With oMenu
&apos;&apos;&apos; .AddItem(&quot;About&quot;, Command := &quot;.uno:About&quot;)
&apos;&apos;&apos; .AddItem(&quot;Run&quot;, Script := &quot;vnd.sun.star.script:Standard.Module1.MyFunction?language=Basic&amp;location=document&quot;)
&apos;&apos;&apos; &apos; MyFunction is a Sub without argument
&apos;&apos;&apos; .Activate()
&apos;&apos;&apos; .Dispose() &apos; Do not dispose() if you plan later on to Activate(Enable := False) the context menu
&apos;&apos;&apos; End With
&apos;&apos;&apos; End Sub
Dim bSettings As Boolean &apos; When True, the menu is already stored
Const cstThisSub = &quot;SFWidgets.ContextMenu.Activate&quot;
Const cstSubArgs = &quot;[Enable=True]&quot;
If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(Enable) Or IsEmpty(Enable) Then Enable = True
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not ScriptForge.SF_Utils._Validate(Enable, &quot;Enable&quot;, ScriptForge.V_BOOLEAN) Then GoTo Catch
End If
If IsNull(ConfigManager) Or IsNull(MenuContainer) Then GoTo Finally
Try:
With ConfigManager
bSettings = .hasSettings(ResourceURL)
If Enable And bSettings Then
.replaceSettings(ResourceURL, MenuContainer)
ElseIf Enable And Not bSettings Then
.insertSettings(ResourceURL, MenuContainer)
ElseIf Not Enable And bSettings Then
.removeSettings(ResourceURL)
Else
&apos; Nothing to deactivate
End If
End With
Finally:
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Sub
Catch:
GoTo Finally
End Sub &apos; SFWidgets.SF_ContextMenu.Activate
REM -----------------------------------------------------------------------------
Public Sub AddItem(Optional ByVal MenuItem As Variant _
, Optional ByVal Command As Variant _
, Optional ByVal Script As Variant _
) As Integer
&apos;&apos;&apos; Insert in the context menu a new entry
&apos;&apos;&apos; Args:
&apos;&apos;&apos; MenuItem: The text to be displayed in the menu entry.
&apos;&apos;&apos; It determines also the hierarchy of the popup menu
&apos;&apos;&apos; It is made up of all the components (separated by the &quot;SubmenuCharacter&quot;) of the menu branch
&apos;&apos;&apos; Example: A&gt;B&gt;C means &quot;C&quot; is a new entry in submenu &quot;A =&gt; B =&gt;&quot;
&apos;&apos;&apos; If the last component is equal to &quot;---&quot;, a line separator is inserted and all other arguments are ignored
&apos;&apos;&apos; Command: A menu command like &quot;.uno:About&quot;. The validity of the command is not checked.
&apos;&apos;&apos; Script: a Basic or Python script (determined by its URI notation) to be run when the item is clicked
&apos;&apos;&apos; Read https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification
&apos;&apos;&apos; No argument will be passed to the called script.
&apos;&apos;&apos; Arguments Command and Script are mutually exclusive.
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; None
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myMenu.AddItem(&quot;Menu top&gt;Item 1&quot;, Command := &quot;About&quot;)
&apos;&apos;&apos; myMenu.AddItem(&quot;Menu top&gt;Item 2&quot;, Script := &quot;vnd.sun.star.script:myLib.Module1.ThisSub?language=Basic&amp;location=document&quot;)
Dim sCommand As String &apos; Alias of either Command or Script
Dim vSplit As Variant &apos; Split menu item
Dim sMenu As String &apos; Submenu where to attach the new item, as a string
Dim oMenu As Object &apos; Submenu where to attach the new item, as an object
Dim sName As String &apos; The text displayed in the menu box
Dim vEntry As Variant &apos; com.sun.star.ui.ItemDescriptor
Const cstThisSub = &quot;SFWidgets.ContextMenu.AddItem&quot;
Const cstSubArgs = &quot;MenuItem, [Command=&quot;&quot;&quot;&quot;], [Script=&quot;&quot;&quot;&quot;]&quot;
If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(Command) Or IsEmpty(Command) Then Command = &quot;&quot;
If IsMissing(Script) Or IsEmpty(Script) Then Script = &quot;&quot;
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not ScriptForge.SF_Utils._Validate(MenuItem, &quot;MenuItem&quot;, V_STRING) Then GoTo Catch
If Not ScriptForge.SF_Utils._Validate(Command, &quot;Command&quot;, V_STRING) Then GoTo Catch
If Not ScriptForge.SF_Utils._Validate(Script, &quot;Script&quot;, V_STRING) Then GoTo Catch
End If
If Len(Command) &gt; 0 Then
If Left(Command, Len(cstUnoPrefix)) = cstUnoPrefix Then sCommand = Command Else sCommand = cstUnoPrefix &amp; Command
Else
sCommand = Script
End If
Try:
&apos; Run through the upper menu tree
vSplit = _SplitMenuItem(MenuItem)
&apos; Create and determine the menu to which to attach the new item
sMenu = vSplit(0)
Set oMenu = _GetContextMenu(sMenu) &apos; Run through the upper menu tree and retain the last branch
&apos; Insert the new item
sName = vSplit(1)
With ScriptForge.SF_Utils
vEntry = Array( ._MakePropertyValue(&quot;Type&quot;, Iif(sName = _SeparatorChar, _
com.sun.star.ui.ItemType.SEPARATOR_LINE, _
com.sun.star.ui.ItemType.DEFAULT)) _
, ._MakePropertyValue(&quot;Label&quot;, Iif(sName = _SeparatorChar, &quot;&quot;, sName)) _
, ._MakePropertyValue(&quot;CommandURL&quot;, sCommand) _
, ._MakePropertyValue(&quot;HelpURL&quot;, &quot;&quot;) _
, ._MakePropertyValue(&quot;Style&quot;, _
Iif(Len(Script) &gt; 0, 0, com.sun.star.ui.ItemStyle.ICON)) _
, ._MakePropertyValue(&quot;ItemDescriptorContainer&quot;, Null) _
)
oMenu.insertByIndex(oMenu.Count, vEntry)
End With
Finally:
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Sub
Catch:
GoTo Finally
End Sub &apos; SFWidgets.SF_ContextMenu.AddItem
REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
&apos;&apos;&apos; Return the actual value of the given property
&apos;&apos;&apos; Args:
&apos;&apos;&apos; PropertyName: the name of the property as a string
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; The actual value of the property
&apos;&apos;&apos; If the property does not exist, returns Null
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; see the exceptions of the individual properties
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; myModel.GetProperty(&quot;MyProperty&quot;)
Const cstThisSub = &quot;SFWidgets.ContextMenu.GetProperty&quot;
Const cstSubArgs = &quot;&quot;
If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
GetProperty = Null
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
End If
Try:
GetProperty = _PropertyGet(PropertyName)
Finally:
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; SFWidgets.SF_ContextMenu.GetProperty
REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos; Return the list of public methods of the Model service as an array
Methods = Array( _
&quot;AddItem&quot; _
, &quot;Execute&quot; _
)
End Function &apos; SFWidgets.SF_ContextMenu.Methods
REM -----------------------------------------------------------------------------
Public Function Properties() As Variant
&apos;&apos;&apos; Return the list or properties of the Timer a.AddItem(&quot;B&gt;B1&quot;)class as an array
Properties = Array( _
&quot;ShortcutCharacter&quot; _
, &quot;SubmenuCharacter&quot; _
)
End Function &apos; SFWidgets.SF_ContextMenu.Properties
REM -----------------------------------------------------------------------------
Public Function SetProperty(Optional ByVal PropertyName As Variant _
, Optional ByRef Value As Variant _
) As Boolean
&apos;&apos;&apos; Set a new value to the given property
&apos;&apos;&apos; Args:
&apos;&apos;&apos; PropertyName: the name of the property as a string
&apos;&apos;&apos; Value: its new value
&apos;&apos;&apos; Exceptions
&apos;&apos;&apos; ARGUMENTERROR The property does not exist
Const cstThisSub = &quot;SFWidgets.ContextMenu.SetProperty&quot;
Const cstSubArgs = &quot;PropertyName, Value&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
SetProperty = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
End If
Try:
SetProperty = _PropertySet(PropertyName, Value)
Finally:
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; SFWidgets.SF_ContextMenu.SetProperty
REM =========================================================== PRIVATE FUNCTIONS
REM -----------------------------------------------------------------------------
Private Function _GetContextMenu(ByVal psSubmenu As String) As Object
&apos;&apos;&apos; Get the context menu node corresponding with the string in argument
&apos;&apos;&apos; A context menu entry is represented by a set of property values
&apos;&apos;&apos; grouped as a ItemDescriptor entry in above array.
&apos;&apos;&apos; The menu node is stored in a com.cun.star.ItemDescriptor service
&apos;&apos;&apos; If the menu entry exists, it is found in the MenuTree dictionary
&apos;&apos;&apos; If it does not exist, it is created recursively.
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psSubmenu: a string like &quot;A&gt;B&quot;
&apos;&apos;&apos; Returns
&apos;&apos;&apos; A com.sun.star.container.XIndexAccess object
&apos;&apos;&apos; Example
&apos;&apos;&apos; If psSubmenu = &quot;A&gt;B&gt;C&gt;D&quot;, and the initial menu container is empty,
&apos;&apos;&apos; - &quot;A&quot;, &quot;A&gt;B&quot;, &quot;A&gt;B&gt;C&quot;, &quot;A&gt;B&gt;C&gt;D&quot; should be created
&apos;&apos;&apos; - the popup menu corresponding with &quot;A&gt;B&gt;C&gt;D&quot; should be returned
Dim oContext As Object &apos; Return value
Dim vSplit As Variant &apos; An array as returned by Split()
Dim sMenu As String &apos; The left part of psSubmenu
Dim vMenu As Variant &apos; Array of property values =&gt; com.sun.star.ui.ItemDescriptor
Dim oLastMenu As Object &apos; com.sun.star.ui.ItemDescriptor.ItemDescriptorContainer
Dim oNewMenu As Object &apos; com.sun.star.ui.ItemDescriptor.ItemDescriptorContainer
Dim i As Long
Set oContext = Nothing
Try:
With ScriptForge.SF_Utils
If Len(psSubmenu) = 0 Then &apos; Menu starts at the root
Set oContext = MenuContainer
ElseIf MenuTree.Exists(psSubmenu) Then &apos; Shortcut: if the submenu exists, get it directly
Set oContext = ._GetPropertyValue(MenuTree.Item(psSubmenu), &quot;ItemDescriptorContainer&quot;)
Else &apos; Build the tree
vSplit = Split(psSubmenu, SubmenuChar)
&apos; Search the successive submenus in the MenuTree dictionary, If not found, create a new entry
Set oLastMenu = MenuContainer
For i = 0 To UBound(vSplit)
sMenu = Join(ScriptForge.SF_Array.Slice(vSplit, 0, i), SubmenuChar)
If MenuTree.Exists(sMenu) Then
Set oNewMenu = ._GetPropertyValue(MenuTree.Item(sMenu), &quot;ItemDescriptorContainer&quot;)
Else
&apos; Insert the new menu tree item at the bottom of the tree branch
Set oNewMenu = MenuContainer.createInstanceWithContext(GetDefaultContext())
vMenu = Array( ._MakePropertyValue(&quot;Type&quot;, com.sun.star.ui.ItemType.DEFAULT) _
, ._MakePropertyValue(&quot;Label&quot;, vSplit(i)) _
, ._MakePropertyValue(&quot;CommandURL&quot;, &quot;&quot;) _
, ._MakePropertyValue(&quot;HelpURL&quot;, &quot;&quot;) _
, ._MakePropertyValue(&quot;Style&quot;, 0) _
, ._MakePropertyValue(&quot;ItemDescriptorContainer&quot;, oNewMenu) _
)
oLastMenu.insertByIndex(oLastMenu.Count, vMenu)
MenuTree.Add(sMenu, vMenu)
End If
Set oLastMenu = oNewMenu
Next i
Set oContext = oLastMenu
End If
End With
Finally:
Set _GetContextMenu = oContext
Exit Function
End Function &apos; SFWidgets.SF_ContextMenu._GetContextMenu
REM -----------------------------------------------------------------------------
Public Sub _Initialize(ByRef poComponent As Object _
, ByVal psContextMenuName As String _
, ByVal psSubmenuChar As String _
)
&apos;&apos;&apos; Complete the object creation process:
&apos;&apos;&apos; - Initialize the dictionary
&apos;&apos;&apos; - initialize the root popup menu
&apos;&apos;&apos; - store the arguments for later use
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Component: the document&apos;s component requesting a context menu
&apos;&apos;&apos; ContextMenuName: a private:resource/popupmenu/... reference
&apos;&apos;&apos; SubmenuChar: Delimiter used in menu trees
Dim oSupplier As Object &apos; /singletons/com.sun.star.ui.theModuleUIConfigurationManagerSupplier
Dim sComponentType As String &apos; Argument to determine the system config manager, ex. &quot;com.sun.star.text.TextDocument&quot;
Dim MainConfigManager As Object &apos; com.sun.star.ui.XUIConfigurationManager
Try:
&apos; Initialize the dictionary
Set MenuTree = ScriptForge.SF_Services.CreateScriptService(&quot;Dictionary&quot;, True) &apos; with case-sensitive comparison of keys
&apos; Identify the container of the menu tree
&apos; The container is taken either from the system configuration manager of from the local (= in document) one
&apos; It is saved in the local version when the menu is Executed()
Set oSupplier = ScriptForge.SF_Utils._GetUNOService(&quot;ModuleUIConfigurationManagerSupplier&quot;)
sComponentType = ScriptForge.SF_UI._GetConfigurationManager(poComponent)
Set MainConfigManager = oSupplier.getUIConfigurationManager(sComponentType)
Set ConfigManager = poComponent.getUIConfigurationManager(sComponentType)
&apos; Copy from system config manager if not found in local (= in document) one
If ConfigManager.hasSettings(psContextMenuName) Then
Set MenuContainer = ConfigManager.getSettings(psContextMenuName, true)
Else
Set MenuContainer = MainConfigManager.getSettings(psContextMenuName, true)
End If
&apos; Store the private instance properties
Set Component = poComponent
ResourceURL = psContextMenuName
If Len(psSubmenuChar) &gt; 0 Then SubmenuChar = psSubmenuChar
Finally:
Exit Sub
End Sub &apos; SFWidgets.SF_ContextMenu._Initialize
REM -----------------------------------------------------------------------------
Private Function _PropertyGet(Optional ByVal psProperty As String) As Variant
&apos;&apos;&apos; Return the value of the named property
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psProperty: the name of the property
Dim vGet As Variant &apos; Return value
Dim cstThisSub As String
Const cstSubArgs = &quot;&quot;
cstThisSub = &quot;SFWidgets.ContextMenu.get&quot; &amp; psProperty
If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
_PropertyGet = Null
Select Case UCase(psProperty)
Case UCase(&quot;ShortcutCharacter&quot;)
_PropertyGet = _UnderlineAccessKeyChar
Case UCase(&quot;SubmenuCharacter&quot;)
_PropertyGet = SubmenuChar
Case Else
_PropertyGet = Null
End Select
Finally:
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; SFWidgets.SF_ContextMenu._PropertyGet
REM -----------------------------------------------------------------------------
Private Function _Repr() As String
&apos;&apos;&apos; Convert the SF_ContextMenu instance to a readable string, typically for debugging purposes (DebugPrint ...)
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Return:
&apos;&apos;&apos; &quot;[ContextMenu]: Name, Menu entries
_Repr = &quot;[ContextMenu]: &quot; &amp; ResourceURL &amp; &quot;, &quot; &amp; SF_String.Represent(MenuTree.Keys())
End Function &apos; SFWidgets.SF_ContextMenu._Repr
REM -----------------------------------------------------------------------------
Private Function _SplitMenuItem(ByVal psMenuItem As String ) As Variant
&apos;&apos;&apos; Split a menu item given as a string and delimited by the submenu character
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psMenuItem: a string like &quot;A&gt;B&gt;C&quot;
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; An array: [0] = &quot;A&gt;B&quot;
&apos;&apos;&apos; [1] = &quot;C&quot;
Dim vReturn(0 To 1) As String &apos; Return value
Dim vMenus() As Variant &apos; Array of menus
Try:
vMenus = Split(psMenuItem, SubmenuChar)
vReturn(1) = vMenus(UBound(vMenus))
vReturn(0) = Left(psMenuItem, Len(psMenuItem) - Iif(UBound(vMenus) &gt; 0, Len(SubmenuChar), 0) - Len(vReturn(1)))
Finally:
_SplitMenuItem = vReturn
End Function &apos; SFWidgets.SF_ContextMenu._SplitMenuItem
REM ============================================ END OF SFWIDGETS.SF_CONTEXTMENU
</script:module>