Mercurial > wow > devian
view Devian.lua @ 33:e6650821a2c0
Debugging pass on console code.
author | Nenue |
---|---|
date | Sun, 27 Dec 2015 02:30:46 -0500 |
parents | c6a2c2df4790 |
children | 7441f3bce940 |
line wrap: on
line source
--- ${PACKAGE_NAME} -- @file-author@ -- @project-revision@ @project-hash@ -- @file-revision@ @file-hash@ if not LibStub then print('Something has happened...') end Devian = LibStub("AceAddon-3.0"):NewAddon("Devian", "AceConsole-3.0", "AceEvent-3.0") local MAJOR, MINOR = 'Devian-1.3', 'r@project-revision@' local D = _G.Devian local WORKSPACE_ON, WORKSPACE_OFF = 1, 2 local PLAYER_REALM = UnitName("player") .. '-' .. GetRealmName() local DEVIAN_FRAME = 'DevianConsole' local DEVIAN_DOCK_FRAME = 'DevianDockFrame' local MSG_NEED_DEV_MODE = 'Must be in development mode to use this function.' local print = _G.print local db local defaults = { ['global'] = {{}, {}}, ['tags'] = {}, ['channels'] = {[1] = {signature = 'Main', index = 1, x = 100, y = 800, height = 500, width = 600, enabled = true}}, primary_channel = 1, -- the channel to which default output is sent current_channel = 1, -- the front channel max_channel = 1, -- the highest created channel id enable = true, -- allow enabled consoles to appear load_message = "Defaults loaded.", -- messages to be displayed after reload font = [[Interface\Addons\Devian\font\SourceCodePro-Regular.ttf]], -- font info fontsize = 13, fontoutline = 'NONE', headergrad = {'VERTICAL', 0, 0, 0, 0.5, 0.1, 0.1, 0.1, 0.3}, -- header info headerdrop = {1,1,1,1}, headeralpha = 1, backdrop = {0,0,0,1}, -- background frame info backgrad = {'VERTICAL', 0.1, 0.1, 0.1, 0.3, 0, 0, 0, 0.5}, backblend = 'MOD', backalpha = 0.7, backborder = {0,0,1,0.75}, frontdrop = {0,0,0,1}, -- foreground frame info frontgrad = {'VERTICAL', 0.1, 0.1, 0.1, 0.9, 0, 0, 0, 0.9}, frontblend = 'MOD', frontalpha = 1, frontborder = {1,0,0,1}, tagcolor = {}, -- tag color repository workspace = 1, -- current profile last_workspace = 2 -- default workspace to alternate with when just "/dvn" is issued } local function ScanAddOnList(cmd, ...) local list_state local args = {} local arg, n = D:GetArgs(cmd, 1) while arg do table.insert(args, arg) arg, n = D:GetArgs(cmd,1,n) end local mode, tag, dest = unpack(args) -- no args, toggle ui if mode == 'dock' then if db.workspace == 1 then D:Print('Must be in dev mode to use this.') return end if #args <= 2 then D:Print("Not enough arguments for dock command.") return end local target local worklist = {} for i = 2, #args do local ch local k = tostring(args[i]) local j = tonumber(args[i]) if db.channels[j] then ch = db.channels[j] elseif D.sig[k] then ch = D.sig[k] elseif D.sigID[k] then ch = db.channels[D.sigID[k]] elseif db.tags[k] and db.tags[k][1] then ch = db.channels[db.tags[j][1]] -- last resort else D:Print('No entry for argument #'..i..': '..tostring(args[i])) return end oldprint(i, '->', ch.index, '-', ch.signature) if i > 2 then table.insert(worklist, ch.index) else target = ch oldprint('arg1', args[2], target) end end D:Print("Docking |cFF88FFFF"..table.concat(worklist, "|r, |cFF88FFFF").."|r with |cFFFFFF00"..target.index..', '..target.signature.."|r.") return D:DockFrame(target.index, unpack(worklist)) elseif mode == 'stack' then if db.workspace == 1 then return D:Print(MSG_NEED_DEV_MODE) end return D:StackFrames() elseif mode == 'grid' then if db.workspace == 1 then return D:Print(MSG_NEED_DEV_MODE) end return D:DistributeFrames() elseif mode == 'tag' then -- tagging if db.workspace == 1 then return D:Print(MSG_NEED_DEV_MODE) end if tag ~= nil and dest ~= nil then -- convert to ID if tonumber(dest) == nil and D.sigID[dest] then dest = db.channels[D.sigID[dest]].index end -- make a new channel? if not db.channels[dest] then dest = db.max_channel + 1 D:Print('Creating a new channel for '.. tag) D:SetChannel(tag, dest) end if db.tags[tag] and db.tags[tag][dest] then db.tags[tag][dest] = nil D:Print('Hiding |cFF88FFFF'..tag..'|r messages in |cFFFFFF00'..db.channels[dest].index ..':'.. db.channels[dest].index) else if not db.tags[tag] then db.tags[tag] = {} end db.tags[tag][dest] = dest D:Print('Showing |cFF88FFFF'..tag..'|r messages in |cFFFFFF00'..db.channels[dest].index ..':'.. db.channels[dest].index) end else D:Print('Usage: /dvn tag <prefix> <console name or number>') end return elseif tonumber(mode) ~= nil or mode == 'save' then -- iterating for something if mode == 'save' then if tonumber(tag) == nil then T:Print('Save ID is invalid:', tag) end list_state = tonumber(tag) else list_state = tonumber(mode) db.workspace = list_state if list_state ~= 1 then db.last_workspace = list_state end end elseif mode == nil then list_state = (db.workspace == 1) and db.last_workspace or 1 db.workspace = list_state db.load_message = "quick swapped workspace #"..list_state else return D:PrintHelp() end -- start the iterating local char_list, global_list = db[PLAYER_REALM][list_state], db.global[list_state] local playername = UnitName("player") for i = 1, GetNumAddOns() do local name = GetAddOnInfo(i) local enableState, globalState = GetAddOnEnableState(playername, i), GetAddOnEnableState(nil, i) if mode == 'save' then char_list[name] = enableState global_list[name] = globalState else if char_list[name] or global_list[name] then if char_list[name] ~= 0 and global_list[name] ~= 0 then local value = false if char_list[name] == 2 and global_list[name] == 1 then value = UnitName("player") elseif global_list[name] == 2 then value = true end --print('EnableAddOn(', i, ',', value,')') EnableAddOn(i, value) else local value = true if char_list[name] == 2 and global_list[name] == 1 then value = UnitName("player") end --print('DisableAddOn(', i, ',', value,')') DisableAddOn(i,value) end elseif mode ~= 'save' then if type(db.unlisted) ~= 'table' then db.unlisted = {} end table.insert(db.unlisted, name) end end end if mode ~= 'save' then --db.load_message = "AddOn profile ".. list_state .." was loaded." ReloadUI() else D:Print('Profile #'.. (list_state)..' saved.') if list_state == 1 then D:Print('This will be your main AddOn list.') elseif list_state == db.default_list then db.last_workspace = list_state D:Print('This will be your default workspace') end end end local function Console_MinMax(self) if self.minimized then self:Maximize() else self:Minimize() end end local function Console_Minimize(self) self:SetHeight(20) self:SetMaxResize(GetScreenWidth(),20) self.minimized = true self.out:Hide() self:Save() end local function Console_Maximize(self) local db = db.channels[self.index] self:SetHeight(db.height) self:SetMaxResize(GetScreenWidth(),GetScreenHeight()) self.minimized = nil self.out:Show() self:Save() end local function Console_Save(self) local db = db.channels[self.index] if self.x then db.x = self.x else db.x = self:GetLeft() end if self.y then db.y = self.y else db.y = (self:GetTop() - GetScreenHeight()) end if self.width then db.width = self.width else db.width = self:GetWidth() end if not self.minimized then if self.height then db.height = self.height else db.height = self:GetHeight() end self:SetHeight(db.height) end db.dockedTo = self.dockedTo db.docked = self.docked db.minimized = self.minimized and true or nil db.enabled = self:IsVisible() and true or nil db.active = self.active and true or nil --print('save:', db.signature, 'min=', db.minimized, ' enabled=', db.enabled, ' active = ', db.active, 'x=', db.x, 'y=', db.y, 'h=', db.height, 'w=', db.width) self:SetPoint('TOPLEFT', UIParent, 'TOPLEFT', db.x, db.y) self:SetWidth(db.width) end -- Console frame toggler -- @paramsig [...] -- @param ... one or more space-seperated channel keys local function Console_Toggle(input) if db.workspace == 1 then return D:Print(MSG_NEED_DEV_MODE) end local search = {} local n = 0 if D:GetArgs(input,1) then repeat key, n = D:GetArgs(input,1,n) if D.sig[key] then table.insert(search, D.sig[key]) elseif D.console[key] then table.insert(search, D.console[key]) end until n == 1e9 else search = D.console end db.enabled = (not db.enabled) and true or nil for i, c in ipairs(search) do --print(i,c.index) if db.enabled then c.enabled = true c:Show() if db.current_channel == c.index then c:ToFront() end c:Save() else c:Hide() end end if db.enabled then D:Print('toggled on?') else D:Print('toggled off?') end end --- Brings the console to the front. -- Frame method used to bring a console frame to the front of the display stack. local function Console_ToFront(c) --print(D.raise_ct, 'Raising', c.signature) --print(unpack(db.frontdrop)) --print(unpack(db.frontgrad)) --print(db.frontblend) -- D.raise_ct = D.raise_ct + 1 c:Raise() c:SetAlpha(db.frontalpha) c.out.backdrop:SetTexture(unpack(db.frontdrop)) c.out.backdrop:SetGradientAlpha(unpack(db.frontgrad)) c.out.backdrop:SetBlendMode(db.frontblend) db.current_channel = c.index for _, part in pairs(c.border) do part:SetTexture(unpack(db.frontborder)) end for id, bc in pairs(D.console) do if id ~= c.index then --print(D.raise_ct, 'Lowering', bc.signature) --print(unpack(db.backdrop)) --print(unpack(db.backgrad)) --print(db.backblend) bc:SetAlpha(db.backalpha) bc.out.backdrop:SetTexture(unpack(db.backdrop)) bc.out.backdrop:SetGradientAlpha(unpack(db.backgrad)) bc.out.backdrop:SetBlendMode(db.backblend) for _, part in pairs(bc.border) do part:SetTexture(unpack(db.backborder)) end end end end local function Console_MouseDown(self, button, up) if button == 'LeftButton' then if up then self:StopMovingOrSizing() self:ToFront() self.x = nil self.y = nil self.width = nil self.height = nil self:Save() elseif self.out.grip:IsMouseOver() then self:StartSizing() else self:StartMoving() end else if up then self:MinMax() end end end local function Console_MouseUp(self, button) return Console_MouseDown(self, button, true) 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. local function Message(prefix, ...) if not db.enabled then return D.oldprint(prefix, ...) end if prefix == nil then prefix = 1 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 local c = { math.random(64,255), math.random(64,255), math.random(64,255) } if c[1] > 223 and c[2] > 223 and c[3] > 223 then c[math.random(1,3)] = math.random(64,223) end db.tagcolor[prefix] = string.format('%02X%02X%02X', unpack(c)) end pcolor = db.tagcolor[prefix] local buffer = {'|cFF'.. pcolor..prefix ..'|r'} for i = 1, select('#',...) do local var = select(i, ...) if type(var) == 'table' then if type(var.GetName) == 'function' then var = '<table:'..var:GetName()..'>' else var = '<'..tostring(var)..'>' end elseif type(var) == 'boolean' then var = var and 'true' or 'false' elseif type(var) == 'function' then var = '<funcref>' elseif type(var) == 'nil' then var = 'nil' end table.insert(buffer, var) end local message = table.concat(buffer, ' ') for id, channel in pairs(sendq) do channel.out:AddMessage(message) end table.wipe(buffer) end --- Constructs the frame object for a console channel -- Initializes the console channel at a specified index. -- Configuration data can be overridden by passing a desired settings table. -- @param i Numeric index of the channel as it manifests in db.channels -- @param vars Optional settings table to be used. local function CreateConsole(i, vars) if tonumber(i) == nil or math.floor(i) ~= i then error('Non-integer index value.') end if not vars then vars = db.channels[i] and db.channels[i] or db.channels[db.primary_channel] end local f if vars.docked then f = CreateFrame('Frame','DevianDockFrame' .. i, DEVIAN_DOCK_FRAME) else f= CreateFrame('Frame', 'DevianChannelFrame' .. i, UIParent, DEVIAN_FRAME) end --@debug@ --print(f:GetName()) --print('create(2)') for k,v in pairs(vars) do f[k] = v --@debug@ --print(' f['..type(k)..' '..tostring(k)..'] = '..type(v)..' '..tostring(v)) end f:SetPoint('TOPLEFT', UIParent, 'TOPLEFT', vars.x, vars.y) f:SetSize(vars.width, vars.height) f:Lower() f.out:SetFont(db.font, db.fontsize, db.fontoutline) if (db.current_channel == i) then f.out.backdrop:SetTexture(unpack(db.frontdrop)) else f.out.backdrop:SetTexture(unpack(db.backdrop)) end f.Save = Console_Save f.Minimize = Console_Minimize f.Maximize = Console_Maximize f.MinMax = Console_MinMax f.ToFront = Console_ToFront f.Toggle = D.Console_Toggle f:SetScript('OnMouseDown', Console_MouseDown) f:SetScript('OnMouseUp', Console_MouseUp) if vars.minimized then f:Minimize() else f:Maximize() end if db.enabled and f.enabled then f:Show() end return f end --- Updates console information and returns the handle of the channel object that was worked on. -- When key is nil or not a valid handle, a new channel is created using whatever signature can be found in cinfo. -- The signature can be passed as a string, or as a table entry under the key 'signature' -- If the signature of a new channel is also a tag, the channel will be added to that tag -- @param cinfo string signature of a new channel, or a table of config variables to be imposed on the channel -- @param key string signature or index number of channel to operate on -- @usage channel = D:SetChannel('new', nil) -- creates a new channel -- @usage channel = D:SetChannel({x = 200, y = 100}, 4) -- updates channel #4 function D:SetChannel(cinfo, key) local t_info = {} local channel, isNew, id, sig, t_id -- obtain source data if tonumber(key) ~= nil and db.channels[key] then id = tonumber(key) elseif D.sigID[tostring(key)] then id = D.sigID[tostring(key)] else id = db.primary_channel isNew = true end local dbvars = db.channels[id] t_id = id -- overridden later if new t_info.index = t_id -- --@debug@ --print('setchan(1) cinfo, key, id=', cinfo, key, id)--@end-debug@ -- obtain config info if type(cinfo) == 'string' then sig = cinfo cinfo = {signature = sig} elseif type(cinfo) ~= 'table' then -- stop here if a table wans't passed error('Expecting table of string as arg1') elseif cinfo.signature then -- new sig sig = cinfo.signature elseif isNew then -- new channel sig sig = 'Ch' else -- old sig sig = db.channels[id].signature end t_info.signature = sig --@debug@ --print('setchan(2) sig,id,isNew=', sig, id, isNew)--@end-debug@ for k,v in pairs(cinfo) do -- allow all cinfo to pass t_info[k] = v end local blocked = { -- ignore these vars: ['docked'] = true, -- table ['dockedTo'] = true, -- table-related ['signature'] = true} -- already determined for k,v in pairs(dbvars) do if not t_info[k] and not blocked[k] then -- already set or blocked? t_info[k] = v end end -- new channel overrides if isNew then if D.sigID[sig]then -- find a non-clashing signature local result, i = sig, 1 while D.sigID[result] do result = sig .. i i = i + 1 end t_info.signature = result end t_id = db.max_channel + 1 t_info.index = t_id --@debug@ --print('setchan(3a) isNew, sig, t_info.signature=', isNew, sig, t_info.signature)--@end-debug@ else --@debug@ --print('setchan(3b) isNew, sig, t_info.signature=', isNew, sig, t_info.signature)--@end-debug@ end local channel if not self.console[t_id] then -- create a frame if isNew then -- position the channel frame t_info.x = t_info.x + 20 t_info.y = t_info.y - 20 db.channels[t_id] = t_info --@debug@ print('setchan(4a)', 't_id, x, y=', t_id, t_info.x, t_info.y)--@end-debug@ end channel = CreateConsole(t_id, t_info) self.console[t_id] = channel self.sig[t_info.signature] = channel self.sigID[t_info.signature] = t_id self.IDsig[t_id] = t_info.signature end channel = self.console[t_id] if channel.minimized then channel:Minimize() else channel:Maximize() end if channel.enabled and db.enabled then -- hide or show last since Min/Max mess with visibility print('setchan(5a) enable') channel:Show() else print('setchan(5a) disable') channel:Hide() end --@debug@ --print('setchan(end); c:IsVisible(), c.enabled, db.enabled=', channel:IsVisible(), channel.enabled, db.enabled)--@end-debug@ return channel 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/resetdvn|r", "- Resets all but profile data SavedVariables.") D:Print("|cFFFFFF00/cleandvn|r", "- Fully resets SavedVariables, profiles and all.") end function D:OnEnable() print(MAJOR, MINOR) if db.unlisted and #db.unlisted > 0 then D:Print('New AddOns have been found since the last profile update: '.. table.concat(db.unlisted, ', ')) table.wipe(db.unlisted) end if db.workspace == 1 then D:Print('Gameplay mode active (list #'..db.workspace..'). Print handling turned |cFFFFFF00OFF|r.') else D:Print('Development mode active (list #'..db.workspace..'). Print handling |cFF00FF00ON|r.') end end function D:OnInitialize() -- commands self:RegisterChatCommand("cleandvn", function(args) DevianDB = nil DevianDB = { load_message = "All SavedVars wiped." } ReloadUI() end) self:RegisterChatCommand("resetdvn", function(args) for k,v in pairs(DevianDB) do if k ~= 'global' then DevianDB[k] = nil end end DevianDB.load_message = "Non-user SavedVars have been reset." for k,v in pairs(defaults) do DevianDB[k] = v end ReloadUI() end) local cmdlist = { ['dvn'] = ScanAddOnList, ['devian'] = ScanAddOnList, ['dvc'] = Console_Toggle, } for cmd, func in pairs(cmdlist) do self:RegisterChatCommand(cmd, func, true) end -- pull defaults if not _G.DevianDB then _G.DevianDB = defaults end db = _G.DevianDB self.tags = db.tags self.channelinfo = db.channels if not db[PLAYER_REALM] then db[PLAYER_REALM] = {[WORKSPACE_ON] = {}, [WORKSPACE_OFF] = {}} end if db.load_message then D:Print(db.load_message) db.load_message = nil end D.oldprint = getprinthandler() if not _G.oldprint then _G.oldprint = D.oldprint end -- Stop here in game mode if db.workspace == 1 then return end self.max_channel = 0 self.num_channels = 0 self.console = {} self.sig = {} self.sigID = {} self.IDsig = {} for i, cinfo in pairs(db.channels) do i = tonumber(i) if not self.primary_channel then self.primary_channel = i end self:SetChannel(cinfo, i) self.max_channel = math.max(i, self.max_channel) self.num_channels = self.num_channels + 1 end for i, channel in pairs(db.channels) do if type(channel.docked) == 'table' then oldprint('docking',i, unpack(channel.docked)) self.DockFrame(i, unpack(channel.docked)) end end if self.console[db.current_channel] then self.console[db.current_channel]:ToFront() -- bring the current channel to the front end setprinthandler(Message) print = function(...) _G.print('Dvn', ...) end end