Xiiph@0: --- **AceConsole-3.0** provides registration facilities for slash commands. Xiiph@0: -- You can register slash commands to your custom functions and use the `GetArgs` function to parse them Xiiph@0: -- to your addons individual needs. Xiiph@0: -- Xiiph@0: -- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by Xiiph@0: -- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object Xiiph@0: -- and can be accessed directly, without having to explicitly call AceConsole itself.\\ Xiiph@0: -- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you Xiiph@0: -- make into AceConsole. Xiiph@0: -- @class file Xiiph@0: -- @name AceConsole-3.0 Xiiph@0: -- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $ Xiiph@0: local MAJOR,MINOR = "AceConsole-3.0", 7 Xiiph@0: Xiiph@0: local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR) Xiiph@0: Xiiph@0: if not AceConsole then return end -- No upgrade needed Xiiph@0: Xiiph@0: AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in. Xiiph@0: AceConsole.commands = AceConsole.commands or {} -- table containing commands registered Xiiph@0: AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable Xiiph@0: Xiiph@0: -- Lua APIs Xiiph@0: local tconcat, tostring, select = table.concat, tostring, select Xiiph@0: local type, pairs, error = type, pairs, error Xiiph@0: local format, strfind, strsub = string.format, string.find, string.sub Xiiph@0: local max = math.max Xiiph@0: Xiiph@0: -- WoW APIs Xiiph@0: local _G = _G Xiiph@0: Xiiph@0: -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded Xiiph@0: -- List them here for Mikk's FindGlobals script Xiiph@0: -- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList Xiiph@0: Xiiph@0: local tmp={} Xiiph@0: local function Print(self,frame,...) Xiiph@0: local n=0 Xiiph@0: if self ~= AceConsole then Xiiph@0: n=n+1 Xiiph@0: tmp[n] = "|cff33ff99"..tostring( self ).."|r:" Xiiph@0: end Xiiph@0: for i=1, select("#", ...) do Xiiph@0: n=n+1 Xiiph@0: tmp[n] = tostring(select(i, ...)) Xiiph@0: end Xiiph@0: frame:AddMessage( tconcat(tmp," ",1,n) ) Xiiph@0: end Xiiph@0: Xiiph@0: --- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function) Xiiph@0: -- @paramsig [chatframe ,] ... Xiiph@0: -- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function) Xiiph@0: -- @param ... List of any values to be printed Xiiph@0: function AceConsole:Print(...) Xiiph@0: local frame = ... Xiiph@0: if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member? Xiiph@0: return Print(self, frame, select(2,...)) Xiiph@0: else Xiiph@0: return Print(self, DEFAULT_CHAT_FRAME, ...) Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: Xiiph@0: --- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function) Xiiph@0: -- @paramsig [chatframe ,] "format"[, ...] Xiiph@0: -- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function) Xiiph@0: -- @param format Format string - same syntax as standard Lua format() Xiiph@0: -- @param ... Arguments to the format string Xiiph@0: function AceConsole:Printf(...) Xiiph@0: local frame = ... Xiiph@0: if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member? Xiiph@0: return Print(self, frame, format(select(2,...))) Xiiph@0: else Xiiph@0: return Print(self, DEFAULT_CHAT_FRAME, format(...)) Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: Xiiph@0: Xiiph@0: Xiiph@0: --- Register a simple chat command Xiiph@0: -- @param command Chat command to be registered WITHOUT leading "/" Xiiph@0: -- @param func Function to call when the slash command is being used (funcref or methodname) Xiiph@0: -- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true) Xiiph@0: function AceConsole:RegisterChatCommand( command, func, persist ) Xiiph@0: if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end Xiiph@0: Xiiph@0: if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk Xiiph@0: Xiiph@0: local name = "ACECONSOLE_"..command:upper() Xiiph@0: Xiiph@0: if type( func ) == "string" then Xiiph@0: SlashCmdList[name] = function(input, editBox) Xiiph@0: self[func](self, input, editBox) Xiiph@0: end Xiiph@0: else Xiiph@0: SlashCmdList[name] = func Xiiph@0: end Xiiph@0: _G["SLASH_"..name.."1"] = "/"..command:lower() Xiiph@0: AceConsole.commands[command] = name Xiiph@0: -- non-persisting commands are registered for enabling disabling Xiiph@0: if not persist then Xiiph@0: if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end Xiiph@0: AceConsole.weakcommands[self][command] = func Xiiph@0: end Xiiph@0: return true Xiiph@0: end Xiiph@0: Xiiph@0: --- Unregister a chatcommand Xiiph@0: -- @param command Chat command to be unregistered WITHOUT leading "/" Xiiph@0: function AceConsole:UnregisterChatCommand( command ) Xiiph@0: local name = AceConsole.commands[command] Xiiph@0: if name then Xiiph@0: SlashCmdList[name] = nil Xiiph@0: _G["SLASH_" .. name .. "1"] = nil Xiiph@0: hash_SlashCmdList["/" .. command:upper()] = nil Xiiph@0: AceConsole.commands[command] = nil Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: --- Get an iterator over all Chat Commands registered with AceConsole Xiiph@0: -- @return Iterator (pairs) over all commands Xiiph@0: function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end Xiiph@0: Xiiph@0: Xiiph@0: local function nils(n, ...) Xiiph@0: if n>1 then Xiiph@0: return nil, nils(n-1, ...) Xiiph@0: elseif n==1 then Xiiph@0: return nil, ... Xiiph@0: else Xiiph@0: return ... Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: Xiiph@0: --- Retreive one or more space-separated arguments from a string. Xiiph@0: -- Treats quoted strings and itemlinks as non-spaced. Xiiph@0: -- @param string The raw argument string Xiiph@0: -- @param numargs How many arguments to get (default 1) Xiiph@0: -- @param startpos Where in the string to start scanning (default 1) Xiiph@0: -- @return Returns arg1, arg2, ..., nextposition\\ Xiiph@0: -- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string. Xiiph@0: function AceConsole:GetArgs(str, numargs, startpos) Xiiph@0: numargs = numargs or 1 Xiiph@0: startpos = max(startpos or 1, 1) Xiiph@0: Xiiph@0: local pos=startpos Xiiph@0: Xiiph@0: -- find start of new arg Xiiph@0: pos = strfind(str, "[^ ]", pos) Xiiph@0: if not pos then -- whoops, end of string Xiiph@0: return nils(numargs, 1e9) Xiiph@0: end Xiiph@0: Xiiph@0: if numargs<1 then Xiiph@0: return pos Xiiph@0: end Xiiph@0: Xiiph@0: -- quoted or space separated? find out which pattern to use Xiiph@0: local delim_or_pipe Xiiph@0: local ch = strsub(str, pos, pos) Xiiph@0: if ch=='"' then Xiiph@0: pos = pos + 1 Xiiph@0: delim_or_pipe='([|"])' Xiiph@0: elseif ch=="'" then Xiiph@0: pos = pos + 1 Xiiph@0: delim_or_pipe="([|'])" Xiiph@0: else Xiiph@0: delim_or_pipe="([| ])" Xiiph@0: end Xiiph@0: Xiiph@0: startpos = pos Xiiph@0: Xiiph@0: while true do Xiiph@0: -- find delimiter or hyperlink Xiiph@0: local ch,_ Xiiph@0: pos,_,ch = strfind(str, delim_or_pipe, pos) Xiiph@0: Xiiph@0: if not pos then break end Xiiph@0: Xiiph@0: if ch=="|" then Xiiph@0: -- some kind of escape Xiiph@0: Xiiph@0: if strsub(str,pos,pos+1)=="|H" then Xiiph@0: -- It's a |H....|hhyper link!|h Xiiph@0: pos=strfind(str, "|h", pos+2) -- first |h Xiiph@0: if not pos then break end Xiiph@0: Xiiph@0: pos=strfind(str, "|h", pos+2) -- second |h Xiiph@0: if not pos then break end Xiiph@0: elseif strsub(str,pos, pos+1) == "|T" then Xiiph@0: -- It's a |T....|t texture Xiiph@0: pos=strfind(str, "|t", pos+2) Xiiph@0: if not pos then break end Xiiph@0: end Xiiph@0: Xiiph@0: pos=pos+2 -- skip past this escape (last |h if it was a hyperlink) Xiiph@0: Xiiph@0: else Xiiph@0: -- found delimiter, done with this arg Xiiph@0: return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1) Xiiph@0: end Xiiph@0: Xiiph@0: end Xiiph@0: Xiiph@0: -- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink) Xiiph@0: return strsub(str, startpos), nils(numargs-1, 1e9) Xiiph@0: end Xiiph@0: Xiiph@0: Xiiph@0: --- embedding and embed handling Xiiph@0: Xiiph@0: local mixins = { Xiiph@0: "Print", Xiiph@0: "Printf", Xiiph@0: "RegisterChatCommand", Xiiph@0: "UnregisterChatCommand", Xiiph@0: "GetArgs", Xiiph@0: } Xiiph@0: Xiiph@0: -- Embeds AceConsole into the target object making the functions from the mixins list available on target:.. Xiiph@0: -- @param target target object to embed AceBucket in Xiiph@0: function AceConsole:Embed( target ) Xiiph@0: for k, v in pairs( mixins ) do Xiiph@0: target[v] = self[v] Xiiph@0: end Xiiph@0: self.embeds[target] = true Xiiph@0: return target Xiiph@0: end Xiiph@0: Xiiph@0: function AceConsole:OnEmbedEnable( target ) Xiiph@0: if AceConsole.weakcommands[target] then Xiiph@0: for command, func in pairs( AceConsole.weakcommands[target] ) do Xiiph@0: target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry Xiiph@0: end Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: function AceConsole:OnEmbedDisable( target ) Xiiph@0: if AceConsole.weakcommands[target] then Xiiph@0: for command, func in pairs( AceConsole.weakcommands[target] ) do Xiiph@0: target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care? Xiiph@0: end Xiiph@0: end Xiiph@0: end Xiiph@0: Xiiph@0: for addon in pairs(AceConsole.embeds) do Xiiph@0: AceConsole:Embed(addon) Xiiph@0: end