view Devian.lua @ 59:5b1c570b1cfd

Added tag v2.1 for changeset 0a9a6740ea5d
author Nenue
date Tue, 12 Jan 2016 04:54:52 -0500
parents 0a9a6740ea5d
children 59e047d6c5de
line wrap: on
line source
--- Devian - Devian.lua
-- @file-author@
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
if not LibStub then
  print('Something has happened...')
end
local MAJOR, MINOR = 'Devian-2.0', 'r@project-revision@'
Devian = LibStub("AceAddon-3.0"):NewAddon("Devian", "AceConsole-3.0", "AceEvent-3.0")
local D = _G.Devian
D:SetDefaultModuleState(false)
D.L = {}
setmetatable(D.L, {__index= function(t,k) return k end, __call = function(t,k,...) return string.format((t[k] or k) , ...) end})
local L = D.L
local pairs, tostring, tonumber, ipairs, type = pairs, tostring, tonumber, ipairs, type
local max, rand, format = math.max, math.random, string.format
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 WORKSPACE_ON, WORKSPACE_OFF = 1, 2
local PLAYER_REALM = UnitName("player") .. '-' .. GetRealmName()
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}},
  ['profiles'] = {
    {'Main', false},
    {'Devian', true},
    {'Turok', true},
    {'Bam', 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
  enabled = 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, 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,
}

local function ScanAddOnList(cmd, ...)
  local list_state

  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
    if db.workspace == 1 then
      return D:Print(L['Need devmode'])
    end
    return D:StackFrames()
  elseif mode == 'grid' then
    if db.workspace == 1 then
      return D:Print(L['Need devmode'])
    end
    return D:DistributeFrames()
  elseif mode == 'tag' then -- tagging
    if db.workspace == 1 then
      return D:Print(L['Need devmode'])
    end

    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 = db.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 D.db.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.db.channels[dest]
      end
      --@debug@
      --print('3 tag,dest,channel.sig=',tag, dest, channel.signature)--@end-debug@

      if not db.tags[tag] then -- no tag table?
        db.tags[tag] = {}
      end

      if db.tags[tag][dest] then -- is tag set?
        db.tags[tag][dest] = nil
        D:Print(L('Tag removed from channel', tag, db.channels[dest].index, db.channels[dest].signature))
      else
        db.tags[tag][dest] = dest
        D:Print(L('Tag added to channel', tag, db.channels[dest].index, db.channels[dest].signature))
      end
      D:UpdateDock()
    else
      D:Print(L['Command tag help'])
    end
    return
  elseif tonumber(mode) ~= nil or mode == 'save' then
    -- iterating for something
    if mode == 'save' then
      if tonumber(tag) == nil then
        T:Print(L('Invalid ID from arg', 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
  else
    return D:PrintHelp()
  end

  -- start the iterating
  if not db[PLAYER_REALM][list_state] then
    db[PLAYER_REALM][list_state] = {}
  end
  if not db.global[list_state] then
    db.global[list_state] = {}
    end

  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
        insert(db.unlisted, name)
      end

    end
  end

  if mode ~= 'save' then
    db.load_message = (mode == nil) and L("Toggled to profile", list_state) or L('Switched to profile number', list_state)
    ReloadUI()
  else

    if list_state == 1 then
      D:Print(L['Profile main saved'])
    else
      db.last_workspace = list_state
      D:Print(L('Profile number saved', list_state, list_state))
    end
  end
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 db.workspace == 1 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
    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))
  end
  pcolor = db.tagcolor[prefix]

  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 = '<funcref>'
    elseif type(var) == 'nil' then
      var = 'nil'
    end

    insert(buffer, var)
  end
  local message = concat(buffer, ' ')
  for id, channel in pairs(sendq) do
    if channel.width < 250 then
      prefix = string.sub(prefix, 0,2)
    end
    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



function D:OnEnable()

  if db.unlisted and #db.unlisted > 0 then
    D:Print('New AddOns have been found since the last profile update: '.. concat(db.unlisted, ', '))
    wipe(db.unlisted)
  end

  if db.workspace == 1 then
    D:Print(L('Devian loaded on standby',MAJOR, MINOR))
  else
    D:Print(L('Devian loaded in workspace', MAJOR, MINOR, db.workspace))
    -- movement fading
    if db.movement_fade then
      self:RegisterEvent('PLAYER_STARTED_MOVING', function()
        for k, v in pairs(self.console) do
          if v.enabled then
            v.preAlpha = v:GetAlpha()
            v:SetAlpha(0)
            v:EnableMouse(false)
          end
        end
      end)
      self:RegisterEvent('PLAYER_STOPPED_MOVING', function()
        for k, v in pairs(self.console) do
          if v.enabled then
            v:SetAlpha(v.preAlpha)
            v:EnableMouse(true)
          end
        end
      end)
    end
  end

  --@debug@
  self:RegisterChatCommand("dvg", function(input)
    if not self.config then
      self.config = DevCon
      self:EnableModule("Config")
    end
    self.modules.Config:ChatCommand(input)
  end)
  --@end-debug@

end

function D:OnInitialize()
  -- commands
  self:RegisterChatCommand("cleandvn", function(args)
    DevianDB = nil
    DevianDB = {
      load_message = "All SavedVars wiped."
    }
    ReloadUI()
    end)
  local blocked = {global = true, [PLAYER_REALM] = true}
  self:RegisterChatCommand("resetdvn", function(args)
    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
    DevianDB.load_message = "Non-user SavedVars have been reset."
    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.db = db
  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.db = db
  self.channels = db.channels
  self.max_channel = 0
  self.num_channels = 0
  self.console = {}
  self.sig = {}
  self.sigID = {}
  self.IDsig = {}
  self.dock = DevianDock
  self.dock.buttons = {}
  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 = max(i, self.max_channel)
    self.num_channels = self.num_channels + 1
  end

  if self.console[db.current_channel] then
    self.console[db.current_channel]:ToFront()
    -- bring the current channel to the front
  end
  DevianDock:Show()
  self:UpdateDock()
  setprinthandler(Message)
  print = function(...)
    _G.print('Dvn', ...)
  end
end

--- Console frame toggler
-- @paramsig [...]
-- @param ... one or more space-seperated channel keys
function D:Console_Toggle(input, force)
  --oldprint(input)
  local setAll
  if db.workspace == 1 then
    return self:Print(MSG_NEED_DEV_MODE)
  end
  local search = {}
  local key
  local n = 0
  while self:GetArgs(input,1,n) and n < 255 do --should end itself when it gets nil, but
      key, n = self:GetArgs(input,1,n)

      if self.sig[key] then
        --print(key, self.sigID[key])
        insert(search, self.sigID[key])
      elseif self.console[tonumber(key)] then
        --print(key, tonumber(key))
        insert(search, tonumber(key))
      end

      --oldprint(#search, key, n)
  end
  if #search < 1 then
    search = self.sigID
    setAll = true
  end
  if setAll then
    --oldprint('setall', setAll)
    db.enabled = (not db.enabled) and true or nil
    if force == 0 then
      db.enabled = nil
    end
  end

  for i, id in pairs(search) do
    --oldprint(i, id)
    local c = self.console[id]
    if setAll then
      c.enabled = db.enabled and db.enabled or nil
    else

      db.enabled = true
      c.enabled = (not c.enabled) and true or nil
      if force == 0 then
        c.enabled = nil
      end
      --oldprint(id, '  ', force, c.enabled, db.enabled)
    end

    if c.enabled or (setAll and db.enabled) then
      c:Show()
      if db.current_channel == c.index then
        c:ToFront()
      end
    elseif not (c.enabled and db.enabled) then
      c:Hide()
    end
    c:Save()
  end

  if setAll then
    if db.enabled then
      self:Print('toggled all consoles ON')
    else
      self:Print('toggled all consoles OFF')
    end
  else
    local result = {}
    for i, id in pairs(search) do
      result[i] = tostring(id) .. ' = ' .. (self.console[id].enabled and 'ON' or 'OFF')
    end
    self:Print('toggled: '..concat(result, ', '))
  end
end