Files
2025-09-29 00:52:08 +02:00

451 lines
12 KiB
Plaintext
Executable File

-- Rockstar Util Menu
-- Rockstar North
-- 1/3/2005
-- by Greg Smith
-- by Luke Openshaw
-- utility functions for creating menus
global RSmacroNameList = undefined
global RSmacroCatList = undefined
-- Struct used to define menu-item for generating TypeNgo data:
struct RsMenuItem
(
Name, Macro, Category, Tags, TopMenuName,
-- Get hash for struct's data, used to avoid showing multiple copies in TypeNGo:
fn GetHash =
(
local Vals = (for Item in #(Name, Macro, Category, Tags) collect (Item as string))
GetHashValue Vals 1
)
)
-- Descriptions of menu-items are added to this list by 'RsAddMenuItems'
-- This list is used to generate a command-list for the TypeNgo tool
global RsMenuItems = #()
global RsMenuItems_Hashes = #()
-- Sets list of defined macro action names and categories, to let us search for action-categories by name:
fn RSgetMacroList nameList:#() catList:#() =
(
local ss = stringstream ""
macros.list to:ss
local macroList = for item in (filterString (ss as string) "\n") collect (filterString item "\"")
::RSmacroNameList = for item in macroList collect (toLower item[2])
::RSmacroCatList = for item in macroList collect item[4]
macroList
)
-- Returns category-name for named macro:
fn RSgetMacroCategory macroName =
(
if (RSmacroNameList == undefined) do
(
RSgetMacroList()
)
local findNum = findItem RSmacroNameList (toLower macroName)
if (findNum == 0) then "" else RSmacroCatList[findNum]
)
-- Runs a named macro: (no need for category-name)
fn RsRunMacro macroName =
(
local macroList = RsGetMacroList()
local macroIdx = findItem RSmacroNameList (toLower macroName)
if (macroIdx == 0) then (return undefined) else
(
local macroLine = macroList[macroIdx]
local macroNum = (trimRight macroLine[1]) as integer
if (macroNum != undefined) do
(
macros.run macroNum
)
)
)
-- Remove menu from main menu
fn RsEmptyMenu menu firstRun:false =
(
for i = menu.numItems() to 1 by -1 do
(
local subMenu
if not firstRun do
(
try (subMenu = (menu.getItem i).getSubMenu()) catch
(
format "\nR* MENU-SETUP SYSTEM-ERROR CAUGHT\nI recommend restarting Max\n"
undefined
)
)
menu.removeItemByPosition i
-- If menuitem is a submenu, unregister it:
if (subMenu != undefined) do
(
local subMenuName = subMenu.getTitle()
menuman.unRegisterMenu subMenu
-- Clear away any duplicates that weren't unregistered previously:
local checkForDupes = true
while checkForDupes do
(
checkForDupes = false
menuExists = (menuMan.findMenu subMenuName)
if (menuExists != undefined) do
(
format "Removing duplicate menu: %\n" subMenuName
menuMan.unRegisterMenu menuExists
checkForDupes = true
)
)
)
)
)
fn RsAddMenuItems menu items reversed:false TopMenuName:undefined StoreInfo:True =
(
-- The quad-menu is upside-down, this allows items to be added in a more sensible order:
local addPos = if reversed then 1 else -1
for item in items do
(
local MenuI
-- If a menu-item has been passed as a RsMenuItem struct (with extra tags, etc)
local InfoItem = undefined
if (isKindOf Item RsMenuItem) do
(
InfoItem = Item
Item = InfoItem.Macro
)
case of
(
-- Add separators:
(item == ""):
(
menui = MenuMan.CreateSeparatorItem()
)
-- Add menu-items, and build TypeNgo data:
(isKindOf Item String):
(
if (InfoItem == undefined) do
(
InfoItem = RsMenuItem Macro:Item
)
if (InfoItem.Category == undefined) do
(
InfoItem.Category = (RSgetMacroCategory item)
)
-- Create menu-item for macro:
MenuI = MenuMan.CreateActionItem InfoItem.Macro InfoItem.Category
-- Collect info-struct if macro exists, and 'StoreInfo' is True:
if (StoreInfo) and (MenuI != undefined) do
(
-- Make sure all info-structs are tagged with the top-level menu-name:
InfoItem.TopMenuName = TopMenuName
-- Get macro's title:
InfoItem.Name = MenuI.GetTitle()
-- Collect info-struct, for use with TypeNgo tool:
append RsMenuItems InfoItem
)
)
-- Recursively build menus from DataPairs:
(isKindOf Item DataPair):
(
::RsSetMenu Item.MenuName Item.Items menuParent:menu reversed:reversed TopMenuName:TopMenuName IsTopLevel:False
)
)
-- Add menuitem, if valid:
if (menui != undefined) do
(
Menu.AddItem menui addPos
)
)
)
-- Add items to quadmenu:
fn RsAddQuadMenuSubItems quadmenu submenuname items =
(
retMenu = menuman.createmenu submenuname
retMenuItem = menuman.createsubmenuitem submenuname retMenu
RsAddMenuItems retMenu items reversed:true
quadmenu.additem retMenuItem 1
return retMenu
)
------------------------------------------------------------------------------------
-- RsClearMenu:
-- Recursively unregisters a given menu-hierarchy
------------------------------------------------------------------------------------
fn RsClearMenu ThisMenu =
(
local ItemCount = ThisMenu.NumItems()
if (ItemCount != 0) do
(
-- Unregister submenus from the bottom up, depth first:
for n = ItemCount to 1 by -1 do
(
local SubItem = ThisMenu.GetItem n
local SubMenu = SubItem.GetSubMenu()
if (SubMenu != undefined) do
(
RsClearMenu SubMenu
MenuMan.UnregisterMenu SubMenu
)
)
-- Make sure the menu contains no items:
for n = 1 to (ThisMenu.NumItems()) do
(
ThisMenu.RemoveItemByPosition 1
)
)
)
------------------------------------------------------------------------------------
-- Tries to find a root menu with the specified name.
-- If menu isnt found, it creates it
------------------------------------------------------------------------------------
fn RsGetNewMenu menuName parentMenu Reversed:False IsTopLevel:False =
(
-- Convert non-menu 'parentMenu' values to appropriate menu-value:
parentMenu = case (classOf parentMenu) of
(
MixinInterface:(parentMenu)
String:(MenuMan.FindMenu parentMenu)
Default:(MenuMan.GetMainMenuBar())
)
-- Find and unregister existing top-level menu(s) with same name:
if IsTopLevel and (isKindOf ParentMenu MixinInterface) do
(
for n = (ParentMenu.NumItems()) to 1 by -1 do
(
local SubMenu = (ParentMenu.GetItem n).GetSubMenu()
if (SubMenu != undefined) and (matchPattern (SubMenu.GetTitle()) pattern:menuName) do
(
RsClearMenu SubMenu
MenuMan.UnregisterMenu SubMenu
)
)
)
-- Create new menu:
local retMenu = MenuMan.CreateMenu menuName
local retMenuItem = MenuMan.CreateSubMenuItem menuName retMenu
-- We add items in reverse for upside-down quadmenus:
local addPos = if reversed then 1 else -1
parentMenu.additem retMenuItem addPos
return retMenu
)
------------------------------------------------------------------------------------
-- Add the passed in items to the menu.
-- The 'items' list will either be a list of macro names,
-- or a datapair defining submenus.
-- 'Reversed' flag adds items in reverse order, used to add to top-quadrant quad-menus
-- 'StoreInfo' flag says whether items will be added to TypeNgo data-list
------------------------------------------------------------------------------------
fn RsSetMenu menuAddTo items menuParent: Reversed:False TopMenuName:undefined StoreInfo:True IsTopLevel:True =
(
-- Add new menu or replace old one with same name:
if (isKindOf menuAddTo String) do
(
-- Replace string 'menuAddTo' with menu generated/found from string:
menuAddTo = RsGetNewMenu (menuAddTo) MenuParent Reversed:Reversed IsTopLevel:IsTopLevel
)
if (IsTopLevel) do
(
TopMenuName = MenuAddTo.GetTitle()
-- Clear command-search data from earlier menu-generations:
local ValidNums = for n = 1 to RsMenuItems.Count where (RsMenuItems[n].TopMenuName != TopMenuName) collect n
RsMenuItems = for n = ValidNums collect RsMenuItems[n]
RsMenuItems_Hashes = for n = ValidNums collect RsMenuItems_Hashes[n]
)
-- Add menuitems:
RsAddMenuItems menuAddTo items reversed:reversed TopMenuName:TopMenuName StoreInfo:StoreInfo
return menuAddTo
)
------------------------------------------------------------------------------------
-- unregisters menus with no content
------------------------------------------------------------------------------------
mapped fn RsCleanMenu menu =
(
local hasContent = false
if (isKindOf menu String) do
(
menu = menuman.findmenu menu
)
local menuItems = for n = 1 to (menu.numItems()) collect (menu.getItem n)
for item in menuItems do
(
local subMenu = item.getSubMenu()
if (subMenu == undefined) then
(
if not item.getIsSeparator() do
(
-- Flag this menu as having content if it contains a non-menu non-separator item
hasContent = true
)
)
else
(
-- Flag this menu as having content if it contains a sub-menu with content
local subMenuHasContent = RsCleanMenu subMenu
if subMenuHasContent do (hasContent = true)
)
)
if not hasContent do
(
-- Don't bother mentioning missing menus to outsourcers:
if (gRsIsOutSource != true) do (format "[Removed Empty Menu: %]\n" menu.getTitle())
-- Remove menus that have no content
menuman.unRegisterMenu menu
)
hasContent
)
------------------------------------------------------------------------------------
-- unregisters menus that are not used by the main menu or quad-menus,
-- and have the same name as another menu.
-- Unused non-duplicate menu-names won't be removed.
-- WARNING:
-- this function triggers unavoidable system-exceptions (inside the Try)
-- which cause the Macro Recorder to stop working for some reason
------------------------------------------------------------------------------------
fn RsRemoveUnusedDupeMenus =
(
-- Add Root menubar to menu-list:
local usedMenus = #(menuMan.getMainMenuBar())
-- Add menus from all quads to menu-list:
for quadNum = 1 to menuMan.numQuadMenus() do
(
local quad = menuMan.getQuadMenu quadNum
for n = 1 to 4 do
(
local getMenu = quad.getMenu n
if (isKindOf getMenu MixinInterface) do
(
appendIfUnique usedMenus (quad.getMenu n)
)
)
)
-- Expand menu-list:
local menuNum = 0
local failCount = 0
local usedMenuNames = #()
local menu, getItem, getMenu
while (menuNum < usedMenus.count) do
(
menuNum += 1
menu = usedMenus[menuNum]
append usedMenuNames (menu.getTitle())
for n = 1 to menu.numItems() do
(
getItem = (menu.getItem n)
getMenu = try (getItem.getSubMenu()) catch -1 -- Some inbuilt context-menus will fail for getSubMenu
case getMenu of
(
(-1):(failCount += 1)
undefined:()
default:(appendIfUnique usedMenus getMenu)
)
)
)
-- Get list of menus that aren't on the usedMenus list:
local unusedMenus = for n = 1 to menuMan.numMenus() collect
(
menu = menuMan.getMenu n
if ((findItem usedMenus menu) == 0) then menu else dontCollect
)
local unusedMenuNames = for menu in unusedMenus collect (menu.getTitle())
local removeMenus = #{}
removeMenus.count = unusedMenus.count
-- Mark any unused items that are named more than once - we'll keep the last one mentioned on the list, and remove the duplicates
local uniqueUnusedNames = #()
local menuName
for menuNum = unusedMenus.count to 1 by -1 do
(
menuName = unusedMenuNames[menuNum]
if ((findItem uniqueUnusedNames menuName) == 0) then
(
append uniqueUnusedNames menuName
)
else
(
removeMenus[menuNum] = true
)
)
-- Mark any remaining menus that have matching names in the used-menus list:
for menuNum = -removeMenus do
(
if ((findItem usedMenuNames unusedMenuNames[menuNum]) != 0) do
(
removeMenus[menuNum] = true
)
)
--format "% menus, % fails, % unused, %(%) total, % to remove\n" usedMenus.count failCount unusedMenus.count (menuMan.numMenus()) (usedMenus.count + unusedMenus.count) removeMenus.numberSet
-- Unregister the menus that have been marked for deletion:
if (removeMenus.numberSet != 0) do
(
format "Removing % unused duplicate menus\n" removeMenus.numberSet
for menuNum = removeMenus do
(
--print (unusedMenus[menuNum].getTitle())
menuman.unRegisterMenu unusedMenus[menuNum]
)
)
ok
)