Nenue@32: --- ${PACKAGE_NAME} Nenue@32: -- @file-author@ Nenue@32: -- @project-revision@ @project-hash@ Nenue@32: -- @file-revision@ @file-hash@ Nenue@0: if not LibStub then Nenue@0: print('Something has happened...') Nenue@0: end Nenue@0: Devian = LibStub("AceAddon-3.0"):NewAddon("Devian", "AceConsole-3.0", "AceEvent-3.0") Nenue@46: local D = _G.Devian Nenue@46: D:SetDefaultModuleState(false) Nenue@46: Nenue@13: local MAJOR, MINOR = 'Devian-1.3', 'r@project-revision@' Nenue@36: local MSG_NEED_DEV_MODE = 'Must be in development mode to use this function.' Nenue@32: local WORKSPACE_ON, WORKSPACE_OFF = 1, 2 Nenue@0: local PLAYER_REALM = UnitName("player") .. '-' .. GetRealmName() Nenue@18: local print = _G.print Nenue@9: local db Nenue@13: local defaults = { Nenue@32: ['global'] = {{}, {}}, Nenue@13: ['tags'] = {}, Nenue@32: ['channels'] = {[1] = {signature = 'Main', index = 1, x = 100, y = 800, height = 500, width = 600, enabled = true}}, Nenue@45: ['profiles'] = { Nenue@45: {'Main', false}, Nenue@45: {'Devian', true}, Nenue@45: {'Turok', true}, Nenue@45: {'Bam', true} Nenue@45: }, Nenue@33: primary_channel = 1, -- the channel to which default output is sent Nenue@33: current_channel = 1, -- the front channel Nenue@33: max_channel = 1, -- the highest created channel id Nenue@33: enable = true, -- allow enabled consoles to appear Nenue@33: load_message = "Defaults loaded.", -- messages to be displayed after reload Nenue@33: font = [[Interface\Addons\Devian\font\SourceCodePro-Regular.ttf]], -- font info Nenue@13: fontsize = 13, Nenue@13: fontoutline = 'NONE', Nenue@45: Nenue@45: headergrad = {'VERTICAL', 0, 0, 0, 1, Nenue@45: 1, 0.1, 0.1, 1}, -- header info Nenue@33: headerdrop = {1,1,1,1}, Nenue@45: headerblend = 'BLEND', Nenue@33: headeralpha = 1, Nenue@45: headerfontcolor = {1,1,1,1}, Nenue@45: Nenue@45: backdrop = {1,1,1,1}, -- background frame info Nenue@45: backgrad = {'VERTICAL', 0.1, 0.1, 0.1, 1, 0, 0, 0, 1}, Nenue@33: backblend = 'MOD', Nenue@45: backalpha = 1, Nenue@45: backborder = {.5,.5,.5,1}, Nenue@45: backheader = {.25,.25,.25,1}, Nenue@45: Nenue@45: frontdrop = {1,1,1,1}, -- foreground frame info Nenue@45: frontgrad = {'VERTICAL', 0.1, 0.1, 0.1, 1, 0, 0, 0, 1}, Nenue@33: frontblend = 'MOD', Nenue@28: frontalpha = 1, Nenue@45: frontborder = {.07,.47,1,1}, Nenue@45: frontheader = {1,1,1,1}, Nenue@33: tagcolor = {}, -- tag color repository Nenue@36: workspace = 2, -- current profile Nenue@33: last_workspace = 2 -- default workspace to alternate with when just "/dvn" is issued Nenue@13: } Nenue@9: Nenue@13: local function ScanAddOnList(cmd, ...) Nenue@0: local list_state Nenue@0: Nenue@14: local args = {} Nenue@14: local arg, n = D:GetArgs(cmd, 1) Nenue@14: while arg do Nenue@14: table.insert(args, arg) Nenue@14: arg, n = D:GetArgs(cmd,1,n) Nenue@14: end Nenue@14: local mode, tag, dest = unpack(args) Nenue@0: Nenue@13: Nenue@14: -- no args, toggle ui Nenue@32: if mode == 'dock' then Nenue@33: if db.workspace == 1 then Nenue@33: D:Print('Must be in dev mode to use this.') Nenue@33: return Nenue@33: end Nenue@32: if #args <= 2 then Nenue@32: D:Print("Not enough arguments for dock command.") Nenue@32: return Nenue@32: end Nenue@0: Nenue@32: local target Nenue@32: local worklist = {} Nenue@32: for i = 2, #args do Nenue@32: local ch Nenue@32: local k = tostring(args[i]) Nenue@32: local j = tonumber(args[i]) Nenue@32: if db.channels[j] then Nenue@32: ch = db.channels[j] Nenue@32: elseif D.sig[k] then Nenue@32: ch = D.sig[k] Nenue@32: elseif D.sigID[k] then Nenue@32: ch = db.channels[D.sigID[k]] Nenue@32: elseif db.tags[k] and db.tags[k][1] then Nenue@32: ch = db.channels[db.tags[j][1]] Nenue@32: -- last resort Nenue@32: else Nenue@32: D:Print('No entry for argument #'..i..': '..tostring(args[i])) Nenue@32: return Nenue@32: end Nenue@35: --@debug@ Nenue@35: --print(i, '->', ch.index, '-', ch.signature)--@end-debug@ Nenue@32: if i > 2 then Nenue@32: table.insert(worklist, ch.index) Nenue@32: else Nenue@32: target = ch Nenue@32: Nenue@35: --@debug@ Nenue@35: --print('arg1', args[2], target)--@end-debug@ Nenue@32: end Nenue@0: end Nenue@32: D:Print("Docking |cFF88FFFF"..table.concat(worklist, "|r, |cFF88FFFF").."|r with |cFFFFFF00"..target.index..', '..target.signature.."|r.") Nenue@32: return D:DockFrame(target.index, unpack(worklist)) Nenue@32: Nenue@32: Nenue@14: elseif mode == 'stack' then Nenue@33: if db.workspace == 1 then Nenue@33: return D:Print(MSG_NEED_DEV_MODE) Nenue@33: end Nenue@14: return D:StackFrames() Nenue@14: elseif mode == 'grid' then Nenue@33: if db.workspace == 1 then Nenue@33: return D:Print(MSG_NEED_DEV_MODE) Nenue@33: end Nenue@14: return D:DistributeFrames() Nenue@14: elseif mode == 'tag' then -- tagging Nenue@33: if db.workspace == 1 then Nenue@33: return D:Print(MSG_NEED_DEV_MODE) Nenue@33: end Nenue@32: Nenue@36: local sig Nenue@14: if tag ~= nil and dest ~= nil then Nenue@36: --@debug@ Nenue@38: --print(tag, dest) Nenue@36: --@end-debug@ Nenue@36: Nenue@32: -- convert to ID Nenue@36: if tonumber(dest) == nil then Nenue@36: if D.sigID[dest] then Nenue@36: dest = db.channels[D.sigID[dest]].index Nenue@36: else Nenue@36: sig = dest Nenue@36: end Nenue@42: else Nenue@42: dest = tonumber(dest) Nenue@18: end Nenue@38: --@debug@ Nenue@38: --print('2 tag,dest,sig', tag, dest, sig)--@end-debug@ Nenue@18: Nenue@32: -- make a new channel? Nenue@36: local channel Nenue@42: if not D.db.channels[dest] then Nenue@36: dest = D.max_channel + 1 Nenue@36: D:Print('Created channel |cFF88FFFF'.. (sig and (dest..':'..sig) or dest ) .. '|r') Nenue@36: channel = D:SetChannel(sig or tag,dest) Nenue@42: else Nenue@42: Nenue@42: channel = D.db.channels[dest] Nenue@32: end Nenue@38: --@debug@ Nenue@42: --print('3 tag,dest,channel.sig=',tag, dest, channel.signature)--@end-debug@ Nenue@32: Nenue@42: if not db.tags[tag] then -- no tag table? Nenue@42: db.tags[tag] = {} Nenue@42: end Nenue@42: Nenue@42: if db.tags[tag][dest] then -- is tag set? Nenue@32: db.tags[tag][dest] = nil Nenue@34: D:Print('Hiding |cFF88FFFF'..tag..'|r messages in |cFFFFFF00'..db.channels[dest].index ..':'.. db.channels[dest].signature) Nenue@32: else Nenue@32: db.tags[tag][dest] = dest Nenue@34: D:Print('Showing |cFF88FFFF'..tag..'|r messages in |cFFFFFF00'..db.channels[dest].index ..':'.. db.channels[dest].signature) Nenue@32: end Nenue@14: else Nenue@14: D:Print('Usage: /dvn tag ') Nenue@14: end Nenue@14: return Nenue@32: elseif tonumber(mode) ~= nil or mode == 'save' then Nenue@32: -- iterating for something Nenue@32: if mode == 'save' then Nenue@32: if tonumber(tag) == nil then Nenue@32: T:Print('Save ID is invalid:', tag) Nenue@32: end Nenue@32: list_state = tonumber(tag) Nenue@32: else Nenue@32: list_state = tonumber(mode) Nenue@32: db.workspace = list_state Nenue@33: if list_state ~= 1 then Nenue@33: db.last_workspace = list_state Nenue@33: end Nenue@33: Nenue@0: end Nenue@32: elseif mode == nil then Nenue@33: list_state = (db.workspace == 1) and db.last_workspace or 1 Nenue@33: db.workspace = list_state Nenue@33: db.load_message = "quick swapped workspace #"..list_state Nenue@32: else Nenue@32: return D:PrintHelp() Nenue@0: end Nenue@32: Nenue@32: -- start the iterating Nenue@36: if not db[PLAYER_REALM][list_state] then Nenue@36: db[PLAYER_REALM][list_state] = {} Nenue@36: end Nenue@36: if not db.global[list_state] then Nenue@36: db.global[list_state] = {} Nenue@36: end Nenue@36: Nenue@0: local char_list, global_list = db[PLAYER_REALM][list_state], db.global[list_state] Nenue@0: local playername = UnitName("player") Nenue@0: Nenue@0: for i = 1, GetNumAddOns() do Nenue@0: local name = GetAddOnInfo(i) Nenue@0: local enableState, globalState = GetAddOnEnableState(playername, i), GetAddOnEnableState(nil, i) Nenue@0: Nenue@32: if mode == 'save' then Nenue@0: char_list[name] = enableState Nenue@0: global_list[name] = globalState Nenue@0: else Nenue@13: if char_list[name] or global_list[name] then Nenue@32: if char_list[name] ~= 0 and global_list[name] ~= 0 then Nenue@32: local value = false Nenue@32: if char_list[name] == 2 and global_list[name] == 1 then Nenue@32: value = UnitName("player") Nenue@32: elseif global_list[name] == 2 then Nenue@32: value = true Nenue@32: end Nenue@32: --print('EnableAddOn(', i, ',', value,')') Nenue@32: EnableAddOn(i, value) Nenue@32: else Nenue@32: local value = true Nenue@32: if char_list[name] == 2 and global_list[name] == 1 then Nenue@32: value = UnitName("player") Nenue@32: end Nenue@32: --print('DisableAddOn(', i, ',', value,')') Nenue@32: DisableAddOn(i,value) Nenue@0: end Nenue@33: elseif mode ~= 'save' then Nenue@32: if type(db.unlisted) ~= 'table' then Nenue@32: db.unlisted = {} Nenue@0: end Nenue@32: table.insert(db.unlisted, name) Nenue@13: end Nenue@0: Nenue@0: end Nenue@0: end Nenue@0: Nenue@32: if mode ~= 'save' then Nenue@33: --db.load_message = "AddOn profile ".. list_state .." was loaded." Nenue@0: ReloadUI() Nenue@0: else Nenue@32: D:Print('Profile #'.. (list_state)..' saved.') Nenue@32: if list_state == 1 then Nenue@32: D:Print('This will be your main AddOn list.') Nenue@32: elseif list_state == db.default_list then Nenue@32: db.last_workspace = list_state Nenue@32: D:Print('This will be your default workspace') Nenue@32: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: Nenue@13: Nenue@18: --- Creates a Devian-style output. Nenue@18: -- The first argument describes the channel to output on, and the remaining arguments are concatenated in a manner similar to default print() Nenue@18: -- This becomes the print handler when development mode is active. The original print() function is assigned to oldprint(). Nenue@18: -- @param Tag, signature, or numeric index of the channel to output on. Defaults to primary channel. Nenue@18: -- @param ... Output contents. Nenue@0: local function Message(prefix, ...) Nenue@45: if db.workspace == 1 then Nenue@23: return D.oldprint(prefix, ...) Nenue@23: end Nenue@23: Nenue@1: if prefix == nil then Nenue@13: prefix = 1 Nenue@1: end Nenue@9: Nenue@18: local sendq = {} Nenue@30: local tag, id, tagged Nenue@13: local byName = true Nenue@18: if D.tags[prefix] then Nenue@18: for _, id in pairs(D.tags[prefix]) do Nenue@18: if D.console[id] then Nenue@18: sendq[id] = D.console[id] Nenue@30: tagged = true Nenue@18: end Nenue@18: end Nenue@18: end Nenue@18: Nenue@13: if D.sig[prefix] then Nenue@18: sendq[D.sig[prefix].index] = D.sig[prefix] Nenue@13: elseif D.console[prefix] then Nenue@18: sendq[D.console[prefix]] = D.console[prefix] Nenue@30: elseif not tagged then Nenue@18: sendq[D.primary_channel] = D.console[D.primary_channel] Nenue@13: end Nenue@18: Nenue@9: -- color me timbers Nenue@9: local pcolor Nenue@18: if (not db.tagcolor[prefix]) and byName then Nenue@30: local c = { Nenue@30: math.random(64,255), math.random(64,255), math.random(64,255) Nenue@30: } Nenue@30: if c[1] > 223 and c[2] > 223 and c[3] > 223 then Nenue@30: c[math.random(1,3)] = math.random(64,223) Nenue@0: end Nenue@30: Nenue@18: db.tagcolor[prefix] = string.format('%02X%02X%02X', unpack(c)) Nenue@0: end Nenue@18: pcolor = db.tagcolor[prefix] Nenue@0: Nenue@18: local buffer = {'|cFF'.. pcolor..prefix ..'|r'} Nenue@0: for i = 1, select('#',...) do Nenue@0: local var = select(i, ...) Nenue@0: Nenue@0: if type(var) == 'table' then Nenue@20: if type(var.GetName) == 'function' then Nenue@46: var = '' Nenue@20: else Nenue@32: var = '<'..tostring(var)..'>' Nenue@20: end Nenue@20: Nenue@0: elseif type(var) == 'boolean' then Nenue@0: var = var and 'true' or 'false' Nenue@0: elseif type(var) == 'function' then Nenue@0: var = '' Nenue@0: elseif type(var) == 'nil' then Nenue@0: var = 'nil' Nenue@0: end Nenue@0: Nenue@0: table.insert(buffer, var) Nenue@0: end Nenue@18: local message = table.concat(buffer, ' ') Nenue@18: for id, channel in pairs(sendq) do Nenue@18: channel.out:AddMessage(message) Nenue@18: end Nenue@0: table.wipe(buffer) Nenue@0: end Nenue@0: Nenue@14: Nenue@32: Nenue@14: Nenue@32: function D:PrintHelp() Nenue@32: D:Print("|cFFFFFF00/dvn|r", Nenue@32: "\n |cFFFFFF00|r - Loads a saved addon list. List 1 is treated as a gameplay profile and consoles will be disabled by default.") Nenue@32: Nenue@32: D:Print("|cFFFFFF00/resetdvn|r", "- Resets all but profile data SavedVariables.") Nenue@32: D:Print("|cFFFFFF00/cleandvn|r", "- Fully resets SavedVariables, profiles and all.") Nenue@32: end Nenue@32: Nenue@45: Nenue@45: Nenue@0: function D:OnEnable() Nenue@33: print(MAJOR, MINOR) Nenue@33: Nenue@33: if db.unlisted and #db.unlisted > 0 then Nenue@33: D:Print('New AddOns have been found since the last profile update: '.. table.concat(db.unlisted, ', ')) Nenue@33: table.wipe(db.unlisted) Nenue@33: end Nenue@33: Nenue@33: if db.workspace == 1 then Nenue@33: D:Print('Gameplay mode active (list #'..db.workspace..'). Print handling turned |cFFFFFF00OFF|r.') Nenue@33: else Nenue@33: D:Print('Development mode active (list #'..db.workspace..'). Print handling |cFF00FF00ON|r.') Nenue@33: end Nenue@33: Nenue@45: self:RegisterChatCommand("dvg", function(input) Nenue@45: if not self.config then Nenue@45: self.config = DevCon Nenue@45: self:EnableModule("Config") Nenue@45: end Nenue@45: self.modules.Config:ChatCommand(input) Nenue@45: end) Nenue@45: Nenue@33: end Nenue@33: Nenue@33: function D:OnInitialize() Nenue@13: -- commands Nenue@33: self:RegisterChatCommand("cleandvn", function(args) Nenue@33: DevianDB = nil Nenue@33: DevianDB = { Nenue@33: load_message = "All SavedVars wiped." Nenue@33: } Nenue@33: ReloadUI() Nenue@33: end) Nenue@36: local blocked = {global = true, [PLAYER_REALM] = true} Nenue@33: self:RegisterChatCommand("resetdvn", function(args) Nenue@33: for k,v in pairs(DevianDB) do Nenue@36: if not blocked[k] then Nenue@33: DevianDB[k] = nil Nenue@33: end Nenue@33: end Nenue@36: for k,v in pairs(defaults) do Nenue@36: if not blocked[k] then Nenue@36: DevianDB[k] = v Nenue@36: end Nenue@36: end Nenue@33: DevianDB.load_message = "Non-user SavedVars have been reset." Nenue@33: ReloadUI() Nenue@33: end) Nenue@13: local cmdlist = { Nenue@13: ['dvn'] = ScanAddOnList, Nenue@13: ['devian'] = ScanAddOnList, Nenue@36: ['dvc'] = "Console_Toggle", Nenue@13: } Nenue@13: for cmd, func in pairs(cmdlist) do Nenue@13: self:RegisterChatCommand(cmd, func, true) Nenue@13: end Nenue@13: Nenue@33: -- pull defaults Nenue@9: if not _G.DevianDB then Nenue@13: _G.DevianDB = defaults Nenue@9: end Nenue@9: db = _G.DevianDB Nenue@32: self.tags = db.tags Nenue@32: self.channelinfo = db.channels Nenue@0: Nenue@0: if not db[PLAYER_REALM] then Nenue@32: db[PLAYER_REALM] = {[WORKSPACE_ON] = {}, [WORKSPACE_OFF] = {}} Nenue@0: end Nenue@0: Nenue@32: if db.load_message then Nenue@32: D:Print(db.load_message) Nenue@32: db.load_message = nil Nenue@0: end Nenue@0: D.oldprint = getprinthandler() Nenue@0: if not _G.oldprint then Nenue@0: _G.oldprint = D.oldprint Nenue@0: end Nenue@13: Nenue@33: -- Stop here in game mode Nenue@33: if db.workspace == 1 then Nenue@33: return Nenue@33: end Nenue@35: ----------------------------------------------------------------------- Nenue@35: self.db = db Nenue@35: self.channels = db.channels Nenue@32: self.max_channel = 0 Nenue@14: self.num_channels = 0 Nenue@13: self.console = {} Nenue@13: self.sig = {} Nenue@14: self.sigID = {} Nenue@14: self.IDsig = {} Nenue@14: for i, cinfo in pairs(db.channels) do Nenue@14: i = tonumber(i) Nenue@14: if not self.primary_channel then Nenue@14: self.primary_channel = i Nenue@14: end Nenue@14: self:SetChannel(cinfo, i) Nenue@32: self.max_channel = math.max(i, self.max_channel) Nenue@32: self.num_channels = self.num_channels + 1 Nenue@32: end Nenue@32: Nenue@32: for i, channel in pairs(db.channels) do Nenue@32: if type(channel.docked) == 'table' then Nenue@38: --oldprint('docking',i, unpack(channel.docked)) Nenue@32: self.DockFrame(i, unpack(channel.docked)) Nenue@14: end Nenue@13: end Nenue@18: Nenue@18: if self.console[db.current_channel] then Nenue@18: self.console[db.current_channel]:ToFront() Nenue@28: -- bring the current channel to the front Nenue@18: end Nenue@46: self.dock = DevianDock Nenue@46: Nenue@46: Nenue@33: setprinthandler(Message) Nenue@33: print = function(...) Nenue@33: _G.print('Dvn', ...) Nenue@23: end Nenue@36: end Nenue@36: Nenue@36: --- Console frame toggler Nenue@36: -- @paramsig [...] Nenue@36: -- @param ... one or more space-seperated channel keys Nenue@36: function D:Console_Toggle(input) Nenue@38: --oldprint(input) Nenue@36: if db.workspace == 1 then Nenue@36: return self:Print(MSG_NEED_DEV_MODE) Nenue@36: end Nenue@36: local search = {} Nenue@36: local n = 0 Nenue@36: if self:GetArgs(input,1) then Nenue@36: repeat Nenue@36: local key, n = self:GetArgs(input,1,n) Nenue@36: if self.sig[key] then Nenue@36: table.insert(search, self.sigID[key]) Nenue@36: elseif self.console[key] then Nenue@36: table.insert(search, tonumber(key)) Nenue@36: end Nenue@36: until n == 1e9 Nenue@36: else Nenue@36: search = self.sigID Nenue@36: end Nenue@36: Nenue@36: db.enabled = (not db.enabled) and true or nil Nenue@36: for i, id in pairs(search) do Nenue@38: --oldprint(i, id) Nenue@36: local c = self.console[id] Nenue@36: if db.enabled then Nenue@36: c.enabled = true Nenue@36: c:Show() Nenue@36: if db.current_channel == c.index then Nenue@36: c:ToFront() Nenue@36: end Nenue@36: c:Save() Nenue@36: else Nenue@36: c:Hide() Nenue@36: end Nenue@36: end Nenue@36: Nenue@36: if db.enabled then Nenue@36: self:Print('toggled on: '..table.concat(search, ', ')) Nenue@36: else Nenue@36: self:Print('toggled off: '..table.concat(search, ', ')) Nenue@36: end Nenue@0: end