<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<muclient>
<plugin
	name="EasyHotkeys"
	author="Ross Grams"
	purpose="Create hotkeys without scripting."
	id="263f7e4e9823a715a199f004"
	language="Lua"
	save_state="y"
	date_written="2019-02-21 14:31:56"
	requires="5.05"
	version="1.2"
	>
</plugin>

<aliases>
	<alias
		script="addHotkey"
		match='^add hotkey (.+?) \"(.+)\" (\d+)(?: (.+))?$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="removeHotkey"
		match='^remove hotkey (.+?)(?: (.+))?$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="hotkeyHelp"
		match='^hotkey help$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="hotkeySendtoList"
		match='^hotkey sendto list$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="hotkeyShowHotkeyList"
		match='^hotkey list$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="enableGroup"
		match='^hotkey enable (.+)$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
	<alias
		script="disableGroup"
		match='^hotkey disable (.+)$'
		enabled="y"
		regexp="y"
		sequence="100"
	></alias>
</aliases>

<triggers>
	<trigger
		enabled="y"
		ignore_case="y"
		keep_evaluating="y"
		match="^hotkey enable (.+)$"
		regexp="y"
		script="enableGroup"
		sequence="100"
	></trigger>
	<trigger
		enabled="y"
		ignore_case="y"
		keep_evaluating="y"
		match="^hotkey disable (.+)$"
		regexp="y"
		script="disableGroup"
		sequence="100"
	></trigger>
</triggers>

<script>
	<![CDATA[

-- "Send To" Codes:

-- The most useful ones are 0:World, 1:Command, 2:Output, 10:Execute, and maybe 12:Script.
local SENDTO = {
	[0] = {"World", "Sends it to the MUD."},
	[1] = {"Command", "Adds it in the input box. (Only if you haven't typed anything yet.)"},
	[2] = {"Output", "Prints it, like using 'frimble', or the Note() function."},
	[3] = {"Status", "Puts it in the status bar at the bottom of the MUSHclient."},
	[4] = {"Notepad (new)", ""},
	[5] = {"Notepad (append)", ""},
	[6] = {"Log File", ""},
	[7] = {"Notepad (replace)", ""},
	[8] = {"Command Queue", ""},
	[9] = {"Send To Variable", ""},
	[10] = {"Execute", "Send to client as if you typed it. Will be checked by other aliases, plugins, etc. before going to the MUD."},
	[11] = {"Speedwalk", "(send text is speedwalk, queue it)"},
	[12] = {"Script", "Execute as code."},
	[13] = {"Immediate", "(send to world in front of speedwalk queue)"},
	[14] = {"Script", "after omit (send to script engine, after lines have been omitted)"},
}

require "json"

local hotkeys = {
	-- [1] = {"Ctrl+M", "minimap ", 1},
}
local groupedHotkeys = {}
local disabledGroups = {}

local txtCol = "white"
local bgCols = { "#303030", "#702020", "" }

local function debugNote(msg, bgColorCode)
	bgColorCode = bgColorCode or 1
	ColourNote(txtCol, bgCols[bgColorCode], msg)
end

local function hotkeyExists(keyCombo, list)
	for i,v in ipairs(list) do
		if v[1] == keyCombo then
			return i
		end
	end
	return false
end

local function groupExists(groupName)
	return groupedHotkeys[groupName]
end

local function ensureGroupExists(groupName)
	if groupExists(groupName) then
		return
	else
		Tell("   ")
		debugNote("Adding new group: '"..groupName.."'.")
		groupedHotkeys[groupName] = {}
	end
end

function addHotkey(name, line, wildcards)
	local keyCombo = wildcards[1]
	local content = wildcards[2]
	local sendTo = tonumber(wildcards[3])
	local groupName = wildcards[4]
	if groupName == "" then  groupName = nil  end
	if groupName then  ensureGroupExists(groupName)  end

	-- Validate sendTo code.
	if sendTo < 0 or sendTo > 14 then
		debugNote(
			"Add Hotkey Failed: Invalid 'sendTo' number: " .. wildcards[3], 2
		)
		return
	end

	-- Print response text.
	local msg =	"Adding Hotkey: " .. keyCombo .. ' - "' .. content ..
		         '", sendTo: ' .. wildcards[3] .. " (" .. SENDTO[sendTo][1] .. ")"
	if groupName then  msg = msg .. ", to group: '"..groupName.."'"  end
	debugNote(msg)

	-- Add hotkey data to list.
	local list = groupName and groupedHotkeys[groupName] or hotkeys

	local existingIndex = hotkeyExists(keyCombo, list)
	if existingIndex then
		local old = list[existingIndex]
		Tell("   ")
		debugNote(
			"Replaces old binding for: "  .. keyCombo .. ' - "' .. old[2] ..
			'", sendTo: ' .. old[3] .. " (" .. SENDTO[old[3]][1] .. ")",
			2
		)
		list[existingIndex][2] = content
		list[existingIndex][3] = sendTo
	else
		table.insert(list, {keyCombo, content, sendTo})
	end
	AcceleratorTo(keyCombo, content, sendTo)
end

function removeHotkey(name, line, wildcards)
	local keyCombo = wildcards[1]
	local groupName = wildcards[2]
	if groupName == "" then  groupName = nil  end
	if groupName and not groupExists(groupName) then
		debugNote("Remove Hotkey Failed. Hotkey group '"..groupName.."' does not exist.", 2)
		return
	end

	local list = groupName and groupedHotkeys[groupName] or hotkeys

	local index = hotkeyExists(keyCombo, list)
	if index then
		local hk = list[index]
		local msg =	"Removing Hotkey: " .. keyCombo .. ' - "' .. hk[2] ..
			         '", sendTo: ' .. hk[3] .. " (" .. SENDTO[hk[3]][1] .. ")"
		if groupName then  msg = msg .. ", from group: '"..groupName.."'"  end
		debugNote(msg)
		Accelerator(keyCombo, "", 0)
		table.remove(list, index)
		if groupName and #list == 0 then
			groupedHotkeys[groupName] = nil -- Remove empty groups.
		end
	else
		debugNote("Remove Hotkey Failed. Couldn't find binding for: " ..
			keyCombo .. ". ", 2 )
	end
end

function hotkeyHelp(name, line, wildcards)
	Note("")
	debugNote("---     Hotkey Plugin Help     ---")
	Note("")
	debugNote(' * To add a hotkey, type: add hotkey <keycombo> "<text to send>" <sendTo code> <group> ')
	debugNote('      For example: add hotkey Ctrl+1 "minimap route 1" 10 ', 3)
	Note("")
	debugNote('      The group is optional. Groups let you easily enable or disable hotkeys. ', 3)
	Note("")
	debugNote(' * To remove a hotkey, type: remove hotkey <keycombo> <group> ')
	debugNote('      For example: remove hotkey Shift+Alt+F1 ', 3)
	Note("")
	debugNote(' * To see a list of "sendTo" codes, type: hotkey sendto list ')
	Note("")
	debugNote(' * To see your current list of hotkeys, type: hotkey list ')
	Note("")
	debugNote('  --  Groups  --  ')
	debugNote('      To enable all hotkeys in a group, type: hotkey enable <group>', 3)
	debugNote('      To disable all hotkeys in a group, type: hotkey disable <group>', 3)
	Note("")
	debugNote('  --  Available Keys  --  ')
	debugNote('Most keys are available, but not quite all. Normal letters,', 3)
	debugNote('numbers, and some punctuation like . , / [ are represented', 3)
	debugNote('by the characters themselves. You can go into the Input menu,', 3)
	debugNote('and click "Key Name..." and use that to test keys and get', 3)
	debugNote('their name (the bottom one). Lastly, you can check here:', 3)
	ColourNote("#888888", "", '   http://www.mushclient.com/scripts/function.php?name=Accelerator')
	debugNote('but not everything on that list works. ', 3)
	Note("")
	debugNote('Note that you can get yourself in trouble by adding hotkeys', 3)
	debugNote('for things like: "a", and have to copy-paste from somewhere', 3)
	debugNote('else to undo it. Or you can break things like copy and paste', 3)
	debugNote('by using those key combos.', 3)
	Note("")
end

function hotkeySendtoList(name, line, wildcards)
	Note("")
	debugNote("--- Hotkey 'sendTo' Codes List ---")
	Note("")
	for i=0,14 do
		local v = SENDTO[i]
		Tell("   ");
		ColourTell(txtCol, bgCols[1], tostring(i) .. " = " .. v[1])
		debugNote(" - " .. v[2], 3)
	end
	Note("")
	debugNote("The most useful ones are probably 0, 1, 2, 10, and maybe 12. ")
	Note("")
end

local function printHotkey(hotkey, indentLevel)
	indentLevel = indentLevel or 1
	Tell(("   "):rep(indentLevel))
	local combo, content, sendTo = unpack(hotkey)
	local str = "[%s] - \"%s\", sendTo: %d (%s)"
	debugNote(str:format(combo, content, sendTo, SENDTO[sendTo][1]))
end

function hotkeyShowHotkeyList(name, line, wildcards)
	Note("")
	debugNote("---      Current Hotkeys:      ---")
	Note("")
	local haveAHotkey = false
	for i,v in ipairs(hotkeys) do
		haveAHotkey = true
		printHotkey(v)
	end
	for name,group in pairs(groupedHotkeys) do
		Tell("  ")
		debugNote("Group '"..name.."':"..(disabledGroups[name] and " (disabled)" or ""))
		for i,v in ipairs(group) do
			haveAHotkey = true
			printHotkey(v, 2)
		end
	end
	if not haveAHotkey then  debugNote("    -- you don't have any --", 3)  end
	Note("")
end

local function getLastHotkey(combo)
	local last
	for i,hotkey in ipairs(hotkeys) do
		if hotkey[1] == combo then
			last = hotkey
		end
	end
	for groupName,group in pairs(groupedHotkeys) do
		if not disabledGroups[groupName] then
			for i,hotkey in ipairs(group) do
				if hotkey[1] == combo then
					last = hotkey
				end
			end
		end
	end
	return last
end

local function enableHotkey(data)
	AcceleratorTo(unpack(data))
end

local function disableHotkey(data, checkForPrevious)
	local combo = data[1]
	if checkForPrevious then
		local lastHotkey = getLastHotkey(combo)
		if lastHotkey then
			enableHotkey(lastHotkey)
		else
			AcceleratorTo(combo, "", 0)
		end
	else
		AcceleratorTo(combo, "", 0)
	end
end

function enableGroup(name, line, wildcards)
	local groupName = wildcards[1]
	if not groupedHotkeys[groupName] then
		debugNote("Enable Hotkey Group Failed. Hotkey group '"..groupName.."' does not exist.", 2)
		return
	end
	local group = groupedHotkeys[groupName]
	disabledGroups[groupName] = nil
	for i,hotkey in ipairs(group) do
		enableHotkey(hotkey)
	end
	debugNote("Hotkey Group '"..groupName.."' enabled.")
end

function disableGroup(name, line, wildcards)
	local groupName = wildcards[1]
	if not groupedHotkeys[groupName] then
		debugNote("Enable Hotkey Group Failed. Hotkey group '"..groupName.."' does not exist.", 2)
		return
	end
	local group = groupedHotkeys[groupName]
	disabledGroups[groupName] = true
	for i,hotkey in ipairs(group) do
		disableHotkey(hotkey, true)
	end
	debugNote("Hotkey Group '"..groupName.."' disabled.")
end

local function initList(input, list, isEnabled)
	for i,hotkey in ipairs(input) do
		list[i] = hotkey
		if isEnabled then  enableHotkey(hotkey)  end
	end
end

local function init()
	debugNote('Hotkey plugin enabled: type "hotkey help" for help.')
	local disabledGroupsStr = GetVariable("disabledGroups")
	if disabledGroupsStr then
		disabledGroups = json.decode(disabledGroupsStr)
	end

	local loaded = GetVariable("hotkeys")
	if loaded then
		loaded = json.decode(loaded)
		initList(loaded, hotkeys, true)
	end

	local groupsStr = GetVariable("groupedHotkeys")
	if groupsStr then
		loadedGroups = json.decode(groupsStr)
		for groupName,loadedGroup in pairs(loadedGroups) do
			ensureGroupExists(groupName)
			local list = groupedHotkeys[groupName]
			initList(loadedGroup, list, not disabledGroups[groupName])
		end
	end
end

local function final()
	for i,hotkey in ipairs(hotkeys) do
		disableHotkey(hotkey)
	end
	for name,group in pairs(groupedHotkeys) do
		for i,hotkey in ipairs(group) do
			disableHotkey(hotkey)
		end
	end
end

function OnPluginSaveState()
	local listStr = json.encode(hotkeys)
	SetVariable("hotkeys", listStr)
	local groupsStr = json.encode(groupedHotkeys)
	SetVariable("groupedHotkeys", groupsStr)
	-- NOTE: Can't save enabled state as a key inside the group because that makes
	--       JSON treat it all like a dictionary with string keys and break everything.
	SetVariable("disabledGroups", json.encode(disabledGroups))
end

function OnPluginInstall()  init()  end
function OnPluginEnable()  init()  end
function OnPluginClose()  final()  end
function OnPluginDisable()  final()  end

	]]>
</script>

</muclient>