Mercurial > wow > devian
view Devian.lua @ 81:c85459c5bb16 v2.1.90
- Fixed AceConsole message mangling
author | Nenue |
---|---|
date | Sun, 10 Jul 2016 06:03:41 -0400 |
parents | 94abbdec03fa |
children | 6e27274da4d9 |
line wrap: on
line source
--- Devian - Devian.lua -- @file-author@ -- @project-revision@ @project-hash@ -- @file-revision@ @file-hash@ --GLOBALS: Devian, DevCon, DevianLoadMessage, DEVIAN_WORKSPACE local ADDON, D = ... local MAJOR, MINOR = 'Devian-2.0', 'r@project-revision@' local D = LibStub("AceAddon-3.0"):NewAddon(D, "Devian", "AceConsole-3.0", "AceEvent-3.0") local L = D.L Devian = D local sub, GetTime, print, _G = string.sub, GetTime, print, _G local format, setmetatable, getprinthandler, setprinthandler = string.format, setmetatable, getprinthandler, setprinthandler local tinsert, tremove, rawset = tinsert, tremove, rawset local currentProfile local playerName = UnitName("player") local playerRealm = playerName .. '-' .. GetRealmName() local num_dock_tabs = 0 DEVIAN_WORKSPACE = false DevianLoadMessage = setmetatable({}, { __call = function(t, msg) rawset(t, #t+1, msg) end, __index = function(t) return #t end }) --@debug@ D.debugmode = true --@end-debug@ D.print = function(...) if currentProfile and not currentProfile.workspace then return end if D.debugmode then return print('Dvn', ...) else return function() end end end local print = D.print D.L = setmetatable({}, { __index= function(t,k) return k end, __call = function(t,k,...) return format((t[k] or k) , ...) end }) D:SetDefaultModuleState(false) D.oldprint = getprinthandler() if not _G.oldprint then _G.oldprint = D.oldprint end local pairs, tostring, tonumber, ipairs, type = pairs, tostring, tonumber, ipairs, type local max, rand, format, print = max, math.random, string.format, print local insert, wipe, concat = table.insert, table.wipe, table.concat local select, unpack = select, unpack local GetNumAddOns, GetAddOnInfo, GetAddOnEnableState, EnableAddOn = GetNumAddOns, GetAddOnInfo, GetAddOnEnableState, EnableAddOn local UnitName, DisableAddOn = UnitName, DisableAddOn local db local defaults = { global = {{}, {}}, default_channel = { signature = 'Main', x = 100, y = 800, height = 500, width = 600, enabled = true}, current_profile = 1, main_profile = 1, last_profile = 1, profilesName = {}, profiles = { }, font = [[Interface\Addons\Devian\font\SourceCodePro-Regular.ttf]], -- font info fontsize = 13, fontoutline = 'NONE', headergrad = {'VERTICAL', 0, 0, 0, 1, 1, 0.1, 0.1, 1}, -- header info headerdrop = {1,1,1,1}, headerblend = 'BLEND', headeralpha = 1, headerfontcolor = {1,1,1,1}, backdrop = {1,1,1,1}, -- background frame info backgrad = {'VERTICAL', 0, 0, 0, .75, 0,0,0, .65}, backblend = 'BLEND', backalpha = 1, backborder = {.5,.5,.5,1}, backheader = {.25,.25,.25,1}, frontdrop = {1,1,1,1}, -- foreground frame info frontgrad = {'VERTICAL', 0, 0, 0, 1, 0,0,0, 0.95}, frontblend = 'BLEND', frontalpha = 1, frontborder = {.07,.47,1,1}, frontheader = {1,1,1,1}, tagcolor = {}, -- tag color repository workspace = 2, -- current profile last_workspace = 2, -- default workspace to alternate with when just "/dvn" is issued dock_onshow_fade_time = 2.5, dock_onshow_fade_from = 1, dock_onshow_fade_to = 0.2, dock_alpha_on = 1, dock_alpha_off = 0.2, dock_fade_in = 0.15, dock_fade_out = 0.45, dock_button_alpha_on = 1, dock_button_alpha_off = 0.2, dock_button_fade_in = 0.075, dock_button_fade_out = 0.075, movement_fade = true, movement_fade_time = 0.15, movement_fade_from = 1, movement_fade_to = 0, movement_translation_x = 25, movement_translation_y = 25, } D.console = {} D.max_channel = 0 D.InWorkspace = function () return db.profiles[db.current_profile].workspace end D.Profile = function (id, name) if name and not id and db.profilesName[name] then id = db.profilesName[name] print('ID located by name, |cFF00FF00'..name..'|r is |cFFFFFF00'.. id..'|r') end if not id or not db.profiles[id] then if not id then id = #db.profiles+1 print('Generated profile ID: |cFFFFFF00'.. id .. '|r') end if not name or db.profilesName[name] then local newName = name or (id == 1 and 'Main' or 'Profile') local prefix = newName local i = 2 while db.profilesName[newName] do i = i + 1 newName = prefix .. i end name = newName print('Generated profile name: |cFF00FF00'..newName..'|r') end print('Creating profile') db.profilesName[name] = id db.profiles[id] = { name = name, workspace = (id ~= 1), current_channel = 1, default_channel = 1, num_channels = 1, max_channel = 1, -- the highest created channel id enabled = true, -- allow enabled consoles to appear channels = { { index = 1, signature = 'Main', x = 100, y = 800, height = 500, width = 600, enabled = true } }, loadouts = {}, global = {}, tags = {}, char = { [playerRealm] = {} }, unlisted = {} } end D.currentProfile = db.profiles[id] currentProfile = D.currentProfile currentProfile.char[playerRealm] = currentProfile.char[playerRealm] or {} if currentProfile.workspace then DEVIAN_WORKSPACE = true setprinthandler(D.Message) else DEVIAN_WORKSPACE = false print = function() end end D.unlisted = currentProfile.unlisted D.channels = currentProfile.channels D.tags = currentProfile.tags D.channelinfo = currentProfile.channels D.char = currentProfile.char[playerRealm] D.global = currentProfile.global D.num_channels = currentProfile.num_channels D.enabled = currentProfile.enabled D.sig = {} D.sigID = {} D.IDsig = {} D.dock = _G.DevianDock D.dock.buttons = D.dock.buttons or {} return id, name end local targetGlobal, targetChar D.Command = function (self, cmd, ...) local list_id, scan_func, reload local args = {} local arg, n = D:GetArgs(cmd, 1) while arg do insert(args, arg) arg, n = D:GetArgs(cmd,1,n) end local mode, tag, dest = unpack(args) -- no args, toggle ui if mode == 'stack' then return D:StackFrames() elseif mode == 'grid' then return D:DistributeFrames() elseif mode == 'tag' then -- tagging return D.Tag(self, tag, dest) elseif mode == 'new' then return D.New(self, tag) elseif mode == 'remove' then return D.Remove(self, tag) elseif tonumber(mode) ~= nil or mode == 'save' then -- either a number of the save command if mode == 'save' then list_id = tonumber(tag) if list_id == nil then D:Print(L('Invalid ID from arg', tag)) end D.Profile(list_id, dest) scan_func = D.Save D:Print("Profile |cFFFFFF00".. list_id .."|r saved.") else D.LoadMessage "Switched profiles." list_id = tonumber(mode) if list_id ~= db.main_profile then db.last_profile = list_id end db.current_profile = list_id scan_func = D.Load end elseif mode == nil then list_id = (db.current_profile ~= db.main_profile) and db.main_profile or db.last_profile D.LoadMessage ("Switched between main and recent profile ("..db.current_profile..' and '..list_id..')') db.current_profile = list_id scan_func = D.Load else return D:PrintHelp() end if not db.profiles[list_id] then db.profiles[list_id] = {global = {}, char = {} } D.LoadMessage ("Starting profile #|cFF00FFFF".. list_id..'|r') end if not db.profiles[list_id].char[playerRealm] then db.profiles[list_id].char[playerRealm] = {} end targetGlobal = db.profiles[list_id].global targetChar = db.profiles[list_id].char[playerRealm] if scan_func then for id, name, enableState, globalState in D.Addons() do scan_func(id, name, enableState, globalState) end end if scan_func == D.Load then _G.ReloadUI() end D.Profile(db.current_profile) end D.Addons = function() local playername = UnitName("player") return function(n, i) if i >= n then return nil end i = i + 1 local name = GetAddOnInfo(i) local enableState, globalState = GetAddOnEnableState(playername, i), GetAddOnEnableState(nil, i) return i, name, enableState, globalState end, GetNumAddOns(), 0 end D.Load = function(id, name, charState, globalState) if targetChar[name] or targetGlobal[name] then if targetGlobal[name] == 2 then EnableAddOn(id, true) elseif targetChar[name] == 2 then EnableAddOn(id, playerName) else if targetChar[name] == 2 and targetGlobal[name] ~= 2 then DisableAddOn(id) else DisableAddOn(id, true) end end --print('load', name, 'global =', targetGlobal[name], 'char =', targetChar[name]) else tinsert(D.unlisted, name) end end D.Save = function(id, name, charState, globalState) --print('save', id, name, charState, globalState) targetGlobal[name] = globalState targetChar[name] = charState end D.Tag = function(self, tag, dest) local sig if tag ~= nil and dest ~= nil then --@debug@ --print(tag, dest) --@end-debug@ -- convert to ID if tonumber(dest) == nil then if D.sigID[dest] then dest = currentProfile.channels[D.sigID[dest]].index else sig = dest end else dest = tonumber(dest) end --@debug@ --print('2 tag,dest,sig', tag, dest, sig)--@end-debug@ -- make a new channel? local channel if not currentProfile.channels[dest] then dest = D.max_channel + 1 D:Print(L('New channel created', sig and (dest..':'..sig) or dest )) channel = D:SetChannel(sig or tag,dest) else channel = D.channels[dest] end --@debug@ --print('3 tag,dest,channel.sig=',tag, dest, channel.signature)--@end-debug@ if not currentProfile.tags[tag] then -- no tag table? currentProfile.tags[tag] = {} end if currentProfile.tags[tag][dest] then -- is tag set? currentProfile.tags[tag][dest] = nil D:Print(L('Tag removed from channel', tag, currentProfile.channels[dest].index, currentProfile.channels[dest].signature)) else currentProfile.tags[tag][dest] = dest D:Print(L('Tag added to channel', tag, currentProfile.channels[dest].index, currentProfile.channels[dest].signature)) end D:UpdateDock() else D:Print(L['Command tag help']) end end D.New = function(self, tag) if tag and not self.sigID[tag] then local id = D.max_channel + 1 D.SetChannel(tag, id) end end D.Remove = function(self, dest) dest = D.sigID[dest] or tonumber(dest) if D.console[dest] and D.channels[dest] then for tag, tagDest in pairs(D.tags) do for i = #tagDest, 0 do -- work downward so we aren't skipping entries if tagDest[i] == dest then tremove(tagDest, i) end end end D.console[dest]:Hide() D.channels[dest] = nil tremove(D.console, dest) tremove(D.dock.buttons, dest) D:UpdateDock() D:Print('Removed channel #'..dest) end end --- Queue up a message to appear after UI reload function D.LoadMessage(msg) tinsert(_G.DevianLoadMessage, msg) end --- Creates a Devian-style output. -- The first argument describes the channel to output on, and the remaining arguments are concatenated in a manner similar to default print() -- This becomes the print handler when development mode is active. The original print() function is assigned to oldprint(). -- @param Tag, signature, or numeric index of the channel to output on. Defaults to primary channel. -- @param ... Output contents. function D.Message(prefix, ...) if not currentProfile.workspace then return D.oldprint(prefix, ...) end prefix = tostring(prefix) if prefix == nil then prefix = 'nil*' end local sendq = {} local tag, id, tagged local byName = true if D.tags[prefix] then for _, id in pairs(D.tags[prefix]) do if D.console[id] then sendq[id] = D.console[id] tagged = true end end end if D.sig[prefix] then sendq[D.sig[prefix].index] = D.sig[prefix] elseif D.console[prefix] then sendq[D.console[prefix]] = D.console[prefix] elseif not tagged then sendq[D.primary_channel] = D.console[D.primary_channel] end -- color me timbers local pcolor if (not db.tagcolor[prefix]) and byName then if prefix:match('^%d+%.%d+$') then pcolor = 'FFFFFF' else local c = { rand(64,255), rand(64,255), rand(64,255) } if c[1] > 223 and c[2] > 223 and c[3] > 223 then c[rand(1,3)] = rand(64,223) end db.tagcolor[prefix] = format('%02X%02X%02X', unpack(c)) pcolor = db.tagcolor[prefix] end else pcolor = db.tagcolor[prefix] end local buffer = {} for i = 1, select('#',...) do local var = select(i, ...) if type(var) == 'table' then if type(var.GetName) == 'function' then var = '[table:'..tostring(var:GetName())..']' else var = '<'..tostring(var)..'>' end elseif type(var) == 'boolean' then var = var and 'true' or 'false' elseif type(var) == 'function' then var = '['..tostring(var)..']' elseif type(var) == 'nil' then var = 'nil' else var = tostring(var) end insert(buffer, var) end local message = concat(buffer, ' ') for id, channel in pairs(sendq) do if channel.width < 250 then prefix = sub(prefix, 0,2) end --currentProfile.last_channel = channel.index channel.out:AddMessage('|cFF'.. pcolor..prefix ..'|r ' .. message, 0.8, 0.8, 0.8, nil, nil, prefix, GetTime()) if not D.dock.buttons[id].newMessage then D.dock.buttons[id].newMessage = true D.UpdateBeacon(D.dock.buttons[id]) end end wipe(buffer) end function D:PrintHelp() D:Print("|cFFFFFF00/dvn|r", "\n |cFFFFFF00<number>|r - Loads a saved addon list. List 1 is treated as a gameplay profile and consoles will be disabled by default.") D:Print("|cFFFFFF00/dvc|r [<key>, ...]", "- Hides and show consoles. A list of channel keys can be passed to specify which ones get toggled.") D:Print("|cFFFFFF00/resetdvn|r", "- Resets all but profile data SavedVariables.") D:Print("|cFFFFFF00/cleandvn|r", "- Fully resets SavedVariables, profiles and all.") end local blocked = {profiles = true, debugmode = true} D.SetDefaults = function() local DevianDB = _G.DevianDB for k,v in pairs(DevianDB) do if not blocked[k] then DevianDB[k] = nil end end for k,v in pairs(defaults) do if not blocked[k] then DevianDB[k] = v end end D.LoadMessage "Non-user SavedVars have been reset." _G.ReloadUI() end D.SetDefaultsAll = function () _G.DevianDB = nil D.LoadMessage "All SavedVars wiped." _G.ReloadUI() end D.UnsetColors = function() db.tagcolor = {} D:Print('Tag color cache cleared.') end D.ConfigCommand = function(self, input) if not self.config then self.config = DevCon self:EnableModule("Config") end self.modules.Config:ChatCommand(input) end function D:OnEnable() -- commands local cmdlist = { ['dfs'] = 'FrameHelper_Update', ['dvn'] = "Command", ['devian'] = "Command", ['dvc'] = "Console_Toggle", ['dvncolors'] = "UnsetColors", ['cleandvn'] = "SetDefaultsAll", ['resetdvn'] = "SetDefaults", ['dvg'] = "ConfigCommand" } for cmd, func in pairs(cmdlist) do self:RegisterChatCommand(cmd, func, true) end end function D:OnInitialize() L = D.L -- pull defaults if not _G.DevianDB then _G.DevianDB = defaults end db = _G.DevianDB self.db = db --- if #_G.DevianLoadMessage >= 1 then for i, msg in ipairs(_G.DevianLoadMessage) do D:Print(msg) end table.wipe(_G.DevianLoadMessage) end --- initialize the current profile local id, name = D.Profile(db.current_profile or 1) D:Print('Using profile |cFFFFFF00'.. id ..'|r: |cFF00FF00'..currentProfile.name..'|r') if currentProfile.workspace then D:Print('Workspace: '.. (#currentProfile.channels) .. ' channels, ' .. #currentProfile.tags .. ' tags.') D:Print('Default channel: |cFFFFFF00'..currentProfile.default_channel..'|r: |cFF00FFFF'.. D.channels[currentProfile.default_channel].signature..'|r') end for i, cinfo in pairs(D.channels) do i = tonumber(i) if not D.primary_channel then D.primary_channel = i end D:SetChannel(cinfo, i) D.num_channels = D.num_channels + 1 end D.max_channel = max(D.max_channel, currentProfile.max_channel) if currentProfile.max_channel < D.max_channel then for i = currentProfile.max_channel, D.max_channel do D.console[i]:Hide() end end if currentProfile.workspace then if D.console[currentProfile.current_channel] then --print('bringing', D.console[currentProfile.current_channel].signature, 'to the front') D.console[currentProfile.current_channel]:ToFront() -- bring the current channel to the front end _G.DevianDock:Show() D:UpdateDock() end end