if AceChatCmd then PerfectRaid.AceChatCmd = AceChatCmd else local AceChatCmd = {} PerfectRaid.AceChatCmd = AceChatCmd local Ace = {} function Ace:print(...) local r,g,b,frame,delay if( type(arg[1]) == "table" ) then r,g,b,frame,delay = unpack(tremove(arg,1)) end (frame or DEFAULT_CHAT_FRAME):AddMessage(self.concat(arg),r,g,b,1,delay or 5) end function Ace.ParseWords(str, pat) if( Ace.tostr(str) == "" ) then return {} end local list = {} for word in gfind(str, pat or "%S+") do tinsert(list, word) end return list end local ACE_CMD = {} ACE_CMD.OPT_HELP = "?" ACE_CMD.OPT_HELP_DESC = "Display extra information about this addon." ACE_CMD.OPT_STANDBY = "standby" ACE_CMD.OPT_STANDBY_DESC = "Toggle the addon's standby mode." ACE_CMD.OPT_REPORT = "report" ACE_CMD.OPT_REPORT_DESC = "Display the status of all settings." ACE_CMD.OPT_INVALID = "Invalid option '%s' entered." ACE_CMD.OPT_LIST_ADDONS = "Addon List" ACE_CMD.OPT_LOAD_IS_LOADED = "%s is already loaded." ACE_CMD.OPT_LOAD_ERROR = "%s could not be loaded because it is %s." ACE_CMD.OPT_LOAD_LOADED = "%s is now loaded." ACE_CMD.OPT_AUTO_OFF_MSG = "%s will no longer be loaded on demand at game start." ACE_CMD.ERROR = "|cffff6060[error]|r" ACE_CMD.ADDON_NOTFOUND = "No addon named '%s' was found." ACE_CMD.ADDON_ENABLED = "%s has been enabled. You must reload the game to load this addon." ACE_CMD.ADDON_ENABLED_ALL = "All addons have been enabled. You must reload the game to load ".. "previously unloaded addons." ACE_CMD.ADDON_DISABLED = "%s has been disabled but will remain loaded until you reload the game." ACE_CMD.ADDON_DISABLED_ALL = "All addons except Ace itself have been disabled but will remain loaded ".. "until you reload the game." ACE_CMD.PROFILE_ADDON_ADDED = "%s has been added. Active profile: %s." ACE_CMD.PROFILE_ALL_ADDED = "All addons have been added. Active profile: %s." ACE_CMD.PROFILE_ALL = "all" ACE_CMD.PROFILE_NO_PROFILE = "%s has no profiling options available." ACE_CMD.USAGE_ADDON_DESC = "|cffffff78[%s v%s]|r : %s" ACE_CMD.USAGE_HEADER = "|cffffff78Usage:|r |cffd8c7ff%s|r %s" ACE_CMD.USAGE_OPT_DESC = " - |cffffff78%s:|r %s" ACE_CMD.USAGE_OPT_SEP = " | " ACE_CMD.USAGE_OPT_OPEN = "[" ACE_CMD.USAGE_OPT_CLOSE = "]" ACE_CMD.USAGE_OPTION = "|cffd8c7ff%s %s|r %s" ACE_CMD.USAGE_NOINFO = "No further information" ACE_CMD.RESULT = "|cffffff78%s:|r %s" ACE_CMD.REPORT_STATUS = "Status" ACE_CMD.REPORT_LINE = "%s [|cfff5f530%s|r]" ACE_CMD.REPORT_LINE_PRE = " - " ACE_CMD.REPORT_LINE_INDENT = " " ACE_CMD.REPORT_NO_VAL = "|cffc7c7c7no value|r" -- Recursively iterate through the table and sub-tables until the entire table -- structure is copied over. Note: I used to check whether the table was numerically -- indexed and use two different for() loops, one with ipairs() and one without. I -- did this because there seemed to be problems otherwise, but in rewriting this, I -- haven't encountered any problems. function Ace.CopyTable(into, from) for key, val in from do if( type(val) == "table" ) then if( not into[key] ) then into[key] = {} end Ace.CopyTable(into[key], val) else into[key] = val end end if (getn(from)) then table.setn(into, getn(from)) end return into end function Ace.GetTableKeys(tbl) local t = {} for key, val in pairs(tbl) do tinsert(t, key) end return(t) end function Ace.TableFindKeyCaseless(tbl, key) key = strlower(key) for i, val in tbl do if( strlower(i) == key ) then return i, val end end end function Ace.TableFindByKeyValue(tbl, key, val, caseless) if( not tbl ) then error("No table supplied to TableFindByKeyValue.", 2) end for i, t in ipairs(tbl) do if( (caseless and (strlower(t[key]) == strlower(val))) or (t[key] == val) ) then return i, t end end end function Ace.concat(t,sep) local msg = "" if( getn(t) > 0 ) then for key, val in ipairs(t) do if( msg ~= "" and sep ) then msg = msg..sep end msg = msg..Ace.tostr(val) end else for key, val in t do if( msg ~= "" and sep ) then msg = msg..sep end msg = msg..key.."="..Ace.tostr(val) end end return msg end function Ace.round(num) return floor(Ace.tonum(num)+.5) end function Ace.sort(tbl, comp) sort(tbl, comp) return tbl end function Ace.strlen(str) return strlen(str or "") end function Ace.tonum(val, base) return tonumber((val or 0), base) or 0 end function Ace.tostr(val) return tostring(val or "") end function Ace.toggle(val) if( val ) then return FALSE end return TRUE end function Ace.trim(str, opt) if( (not opt) or (opt=="left" ) ) then str = gsub(str, "^%s*", "") end if( (not opt) or (opt=="right") ) then str = gsub(str, "%s*$", "") end return str end -- Object constructor function AceChatCmd:new(commands,options) self.__index = self return setmetatable({_cmdList=commands,options=options}, self) end --[[--------------------------------------------------------------------------------- Reporting Methods ------------------------------------------------------------------------------------]] function AceChatCmd:result(...) Ace:print(self.app.disabled and ACE_ADDON_STANDBY.." " or "", format(ACE_CMD.RESULT, self.app.name, Ace.concat(arg)) ) end function AceChatCmd:msg(...) self:result(format(unpack(arg))) end function AceChatCmd:error(...) self:result(ACE_CMD.ERROR.." ", format(unpack(arg))) end function AceChatCmd:status(text, val, map) if( map ) then val = map[val or 0] or val end self:result(text, " ", ACE_TEXT_NOW_SET_TO, " ", format(ACE_DISPLAY_OPTION, val or ACE_CMD.REPORT_NO_VAL) ) end function AceChatCmd:report(hdr, def) if( not def ) then def = hdr; hdr = nil end Ace:print(hdr or format(ACE_CMD.RESULT, self.app.name.." "..ACE_CMD.REPORT_STATUS, self.app.disabled and ACE_ADDON_STANDBY or "" ) ) for _, ref in ipairs(def) do local val if( ref.map ) then val = ref.map[ref.val or 0] else val = ref.val end Ace:print( (ref.indent and (strrep(" ", strlen(ACE_CMD.REPORT_LINE_PRE)).. strrep(ACE_CMD.REPORT_LINE_INDENT, ref.indent)) or ACE_CMD.REPORT_LINE_PRE ), format(ACE_CMD.REPORT_LINE, ref.text, val or ACE_CMD.REPORT_NO_VAL) ) end end --[[--------------------------------------------------------------------------------- Command Handling Methods ------------------------------------------------------------------------------------]] function AceChatCmd:FindCommand(cmd) cmd = strlower(cmd) for index in SlashCmdList do local i, cmdString = 0 repeat i = i + 1 cmdString = getglobal("SLASH_"..index..i) if( strlower(cmdString or "") == cmd ) then return TRUE end until( not cmdString ) end end function AceChatCmd:Register(handler) if( not self._cmdList ) then error("Attempt to register command handler without defining commands.", 2) end if( self.registered ) then return end local slashID = strupper(self.app.name).."_CMD" SlashCmdList[slashID] = function(msg) self:ProcessCommand(msg) end local i = 0 for index, cmd in self._cmdList do if( not self:FindCommand(cmd) ) then if( not self.commands ) then self.commands = {} end i = i + 1 tinsert(self.commands, cmd) setglobal("SLASH_"..slashID..i, cmd) end end self.handler = handler or self if( not self.options ) then self.options = {} end if( self.handler.Report ) then tinsert(self.options, 1, { option = ACE_CMD.OPT_REPORT, desc = ACE_CMD.OPT_REPORT_DESC, method = "Report" } ) end if( self.app.Enable ) then tinsert(self.options, 1, { option = ACE_CMD.OPT_STANDBY, desc = format(ACE_CMD.OPT_STANDBY_DESC, self.app.name), method = "ToggleStandBy" } ) end tinsert(self.options, 1, {option = ACE_CMD.OPT_HELP, desc = ACE_CMD.OPT_HELP_DESC}) -- Create a closure for printing option lines, since it's done so much self.printOpt = function(l,t) Ace:print(format(ACE_CMD.USAGE_OPT_DESC,l,t)) end self.registered = TRUE end function AceChatCmd:ProcessCommand(msg) msg = Ace.trim(msg or "") if( strfind(strlower(msg), ACE_CMD.OPT_HELP) == 1 ) then self:DisplayAddonInfo(self.options) return elseif( msg ~= "" ) then return self:ProcessOptions(self.options, msg, self.handler, self.handler.CommandHandler) elseif( self.handler.CommandHandler ) then self.handler.CommandHandler(self.handler, msg) return end self:DisplayUsage() end function AceChatCmd:ProcessOptions(list, msg, handler, method, opttext) local _, _, cmd = strfind(strlower(msg), "(%S+)") local args = gsub(msg, cmd.."%s*", "") local i = Ace.TableFindByKeyValue(list, "option", cmd, TRUE) if( i ) then local opt = list[i] if( opt.args and (args ~= "") and self:ProcessOptions(opt.args, args, opt.handler or handler, opt.method or method, opttext and opttext.." "..opt.option or opt.option ) ) then return TRUE elseif( opt.method and ((args ~= "") or (not opt.input)) and (strlower(args) ~= ACE_CMD.OPT_HELP) ) then handler = opt.handler or handler handler[opt.method](handler, args) return TRUE elseif( opt.input and (args == "") or (opt.args and (not method)) or (strlower(args) == ACE_CMD.OPT_HELP) ) then self:OptionHeader(opt, opttext) self:DisplayArgUsage(opt) return TRUE end elseif( ((cmd or "") ~= "") and (cmd ~= ACE_CMD.OPT_HELP) and (not method) ) then self:result(format(ACE_CMD.OPT_INVALID, cmd)) return TRUE end end --[[--------------------------------------------------------------------------------- Usage Display Methods ------------------------------------------------------------------------------------]] function AceChatCmd:DisplayAddonInfo() self:CommandHeader() if( self.commands ) then self.printOpt(ACE_TEXT_COMMANDS, Ace.concat(self.commands, " | ")) end if( self.app.author ) then self.printOpt(ACE_TEXT_AUTHOR, self.app.author) end if( self.app.releaseDate ) then self.printOpt(ACE_TEXT_RELEASED, self.app.releaseDate) end if( self.app.email ) then self.printOpt(ACE_TEXT_EMAIL, self.app.email) end if( self.app.website ) then self.printOpt(ACE_TEXT_WEBSITE, self.app.website) end end function AceChatCmd:OptionHeader(option, opttext) local args if( option.args ) then args = self:ConcatOptions(option.args, ACE_CMD.USAGE_OPT_SEP) end if( not args ) then args = "- "..(option.desc or ACE_CMD.USAGE_NOINFO) end Ace:print(format(ACE_CMD.USAGE_OPTION, self.commands[1], opttext and opttext.." "..option.option or option.option, args ) ) end function AceChatCmd:DisplayArgUsage(option) if( not option.args ) then return end for _, arg in ipairs(option.args) do self.printOpt(arg.option, arg.desc) end end function AceChatCmd:DisplayUsage() self:CommandHeader() if( getn(self.options) < 1 ) then return end Ace:print(format(ACE_CMD.USAGE_HEADER, self.commands[1], self:ConcatOptions(self.options, ACE_CMD.USAGE_OPT_SEP) ) ) -- Have to print the lines one at a time or risk it getting too large for the -- print buffer. for _, opt in ipairs(self.options) do self.printOpt(opt.option, opt.desc) end end function AceChatCmd:CommandHeader() Ace:print(self.app.disabled and ACE_ADDON_STANDBY.." " or "", format(ACE_CMD.USAGE_ADDON_DESC, self.app.name, self.app.version, self.app.description ) ) end function AceChatCmd:ConcatOptions(options, sep) local str = "" for _, opt in options do if( (str ~= "") and sep ) then str = str..sep end str = str..opt.option end return str end end